当前位置:   article > 正文

ESP8266+STM32获取网络时间、OLED显示时间&图片&视频。

stm32获取网络时间

学习过程不易,发文共享以下学习过程~

先说说我的设计内容的组成:

目录

学习过程不易,发文共享以下学习过程~

1. STM32控制ESP8266获取网络时间

第一步:电脑控制ESP8266获取时间数据;

2. STM32基于获取到的时间使用定时器本地运行

3. 使用OLED显示时间数据,包括自定义的文字显示,图片显示,视频显示;

4. 完整的工程代


1. STM32控制ESP8266获取网络时间

为了更好的理解原理,可以先从电脑通过串口控制ESP8266获取时间数据开始入手学习。

为此需要准备以下的软件和硬件

1.ESP8266-01S(其他ESP8266应该也可以)

2.一个烧录下载器

3.正点原子的串口助手

 

第一步:电脑控制ESP8266获取时间数据;

需要用ESP8266和电脑建立串口通讯,通过电脑给ESP8266发送AT指令。

插一句关于AT指令的介绍:

我的理解是 AT指令就是一句句的口令,你向ESP8266发出对应的指令,ESP8266就会根据你的指令做出相应的反应,当然AT指令是一系列提前编译好的官方指令,不是自己随意编写的!对于AT指令的学习,可以参考这篇文章~

玩转ESP8266-01——AT指令集  http://t.csdn.cn/1EZ0E

 Step1:将ESP8266和烧录下载器正确连接,再与电脑连接

2dfdfbb1567b4f3d8d0f66976320e0c4.jpeg

烧录下载器和电脑

a85daf91437744a38c8f58d4e2a0d5c7.jpeg

ESP8266和烧录下载器

 

  Step2:打开正点原子的串口助手,选择正确的端口号,波特率选择115200.(当然你要是电脑没有烧录下载器的驱动的话,电脑会识别不了这个设备,必须去安装CH340的驱动才行)

1b87385cb2d54c8083b1eddf1ec08ca6.png

初始界面

正确连接的话,上述界面由设备的端口号。 

Step3:提前新建好对应的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. +++ //退出透传模式

 完成上述的准备工作之后,就可以点击打开串口、发送新行。

一般串口刚连接上会发送一些杂乱数据(应该是表示ESP8266准备好了通讯的意思,因为发来的消息最后面有个ready)

Step4:按照顺序一次的点击新建好的10条AT指令,当然速度不适宜太快,需要等ESP8266发送对应的回答之后才可以继续点击,每一次点击的 效果如下。


234afc1e6a224d4ca1a70f6b25a4968c.png

第一条指令:

3103fd14ac35484c8b3fb3f9334ff732.png

第二条指令:


856f4c43245e4845b715afa65ce78270.png

第三条指令:

fc78e154d16743c28828b4a61ea046ed.png

第四条指令:

 


65f910f9da2b4a629a885800dbcde57d.png

第五条指令:

2fb3c0345fb64dcf8b825b47e7264ca0.png

第六条指令:


f5aad0845a22442ea98551b7825bca21.png

第八条指令:

99d0b64a810a4dd49314e9f90f2ac76c.png

第七条指令:

 


28b3c8df8dbd43cca5509e921e1aa7cc.png

第九条指令:

063fb794a3834416955db435fca89b03.png

第十条指令:

 

补充一下上面ESP8266返回来的数据说明:

 第一张图片:如果返回来OK表示器件正确的连接了。

 第二张图片:收到OK就表示复位成功,后面跟着的应该是一些ESP8266的自身信息。

 第三张图片:将ESP8266切换为STA模式,作为一个设备去连接WIFI,关于ESP8266的工作模式可以看这个链接http://t.csdn.cn/4x1oA

 第四张图片:设置ESP8266为单路连接模式,返回OK表示开启成功。

 第五张图片:注意这是表示将要连接的WIFI的名称和密码发送给ESP8266,要注意大小写,最好是用手机开热点,这样方便查看是否成功连接上,返回OK即连接成功。

 第六张图片:如果返回来OK表示ESP8266开启透传模式,透传模式不懂的也可以看上面的链接。

 第七张图片:通过苏宁去获取时间,这是苏宁的服务器地址,返回OK连接成功。

 第八张图片:查询ESP8266是否准备发送就绪,出现一个>表示已就绪。

 第九张图片:这是一个返回时间的链接,在浏览器输入网址浏览器即返回时间数据。

 第十张图片:退出透传模式,要先关闭发送新行,然后多试几次,如果收到+++表示退出成功,这个时候再点击第一条指令,收到OK就验证了退出成功。

 

2. STM32基于获取到的时间使用定时器本地运行

了解了ESP8266获取时间的原理之后就可以开始编写STM32F103C8T6的工程了,

这里是使用一个ESP8266的库来开发ESP8266的,代码原文是这个~,感谢大佬的分享。

http://t.csdn.cn/L3M3nhttp://t.csdn.cn/L3M3n本来是想基于上述文章,写一个获取时间天气的课设的,在一番尝试之后,无果,临近检查,就只能复刻一下只获取时间数据了。

获取时间天气的JSON解析函数我也写了,为此特定去学了以下JSON解析,参考一篇知乎的文章写的,链接找不到了,贴出我的解析函数,大家可以帮我参考一下。

这里我仿照上面大佬的文章,将变量用 函数的方法返回到主函数,奈何还是有问题,有时间再经研究吧。

  1. char id,city,guojia,zone,tianqi,code,temp,time;
  2. //心知天气
  3. void parse_seniverse_weather(void)
  4. {
  5. cJSON *root;
  6. cJSON *results;
  7. cJSON *last_update;
  8. cJSON *loc_json, *daily_json;
  9. cJSON *forecast_json;
  10. char *loc_tmp, *weather_tmp, *update_tmp;
  11. root = cJSON_Parse((const char*)Time_buff); //root就取得了json的数据
  12. if(root) //如果取得
  13. {
  14. // printf("JSON格式正确:\n%s\n\n",cJSON_Print(root)); //输出json字符串
  15. results = cJSON_GetObjectItem(root, "results"); //寻找关键字一
  16. results = cJSON_GetArrayItem(results,0); //取出results数组里的第0个也就是第一个
  17. if(results) //如果取得
  18. {
  19. loc_json = cJSON_GetObjectItem(results, "location"); //得到location键对应的值,是一个对象
  20. loc_tmp = cJSON_GetObjectItem(loc_json, "id") -> valuestring; //取得ID
  21. id = *loc_tmp; //注意这个地方可能出错,看读取出来的是什么
  22. loc_tmp = cJSON_GetObjectItem(loc_json, "name") -> valuestring; //取得城市
  23. city = *loc_tmp;
  24. loc_tmp = cJSON_GetObjectItem(loc_json, "country") -> valuestring; //取得国家
  25. guojia = *loc_tmp;
  26. loc_tmp = cJSON_GetObjectItem(loc_json, "timezone") -> valuestring; //取得时区
  27. zone = *loc_tmp;
  28. daily_json = cJSON_GetObjectItem(results, "now");//原本是读取三天的,修改为读取现在的now
  29. if(daily_json)
  30. {
  31. forecast_json = cJSON_GetArrayItem(daily_json, 0);
  32. weather_tmp = cJSON_GetObjectItem(forecast_json, "text") -> valuestring; //取得天气情况
  33. tianqi = *weather_tmp;
  34. weather_tmp = cJSON_GetObjectItem(forecast_json, "code") -> valuestring; //取得code情况
  35. code = *weather_tmp;
  36. weather_tmp = cJSON_GetObjectItem(forecast_json, "temperature") -> valuestring; //取得温度情况
  37. temp = *weather_tmp;
  38. }
  39. else
  40. // printf("daily json格式错误\r\n");
  41. last_update = cJSON_GetObjectItem(results, "last_update");
  42. update_tmp = last_update->valuestring;
  43. if(last_update)
  44. {
  45. time = *update_tmp;
  46. }
  47. }
  48. else
  49. {
  50. //printf("results格式错误:%s\r\n", cJSON_GetErrorPtr());
  51. }
  52. }
  53. else
  54. {
  55. //printf("JSON格式错误\r\n");
  56. }
  57. cJSON_Delete(root);//最后释放空间
  58. cJSON_Delete(results);//最后释放空间
  59. }

重点来了,我的课设的代码,其实和大佬的分享差不多。

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

另一个重头戏来了。

OLED显示!!

3. 使用OLED显示时间数据,包括自定义的文字显示,图片显示,视频显示;

OLED显示我参考了很多资料,修修改改还是完成的七七八八。

OLED显示有自定义的编写字库的软件,编写图片字库的软件,

取字模的软件用的是:

a974b2bd25f445f98b92ef7e4eaa09f6.png

取字模

e0fec89b5c524718881e8181ff323792.png

分离抽帧

 

完蛋!刚刚取找文件,发现我把文件删了,如果有人着急的话,我可以付费帮忙解决一下,很好用的,可以一键将所有图片按照名称分成一个个的记事本文件,只用复制粘贴即可。

看看实物效果先:

ikun

 

我的OLED.C文件

  1. #include "stm32f10x.h"
  2. #include "OLED_Font.h"
  3. /*引脚配置*/
  4. #define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
  5. #define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
  6. /*引脚初始化*/
  7. void OLED_I2C_Init(void)
  8. {
  9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  10. GPIO_InitTypeDef GPIO_InitStructure;
  11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  12. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  14. GPIO_Init(GPIOB, &GPIO_InitStructure);
  15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  16. GPIO_Init(GPIOB, &GPIO_InitStructure);
  17. OLED_W_SCL(1);
  18. OLED_W_SDA(1);
  19. }
  20. /**
  21. * @brief I2C开始
  22. * @param 无
  23. * @retval 无
  24. */
  25. void OLED_I2C_Start(void)
  26. {
  27. OLED_W_SDA(1);
  28. OLED_W_SCL(1);
  29. OLED_W_SDA(0);
  30. OLED_W_SCL(0);
  31. }
  32. /**
  33. * @brief I2C停止
  34. * @param 无
  35. * @retval 无
  36. */
  37. void OLED_I2C_Stop(void)
  38. {
  39. OLED_W_SDA(0);
  40. OLED_W_SCL(1);
  41. OLED_W_SDA(1);
  42. }
  43. /**
  44. * @brief I2C发送一个字节
  45. * @param Byte 要发送的一个字节
  46. * @retval 无
  47. */
  48. void OLED_I2C_SendByte(unsigned char Byte)
  49. {
  50. unsigned char i;
  51. for (i = 0; i < 8; i++)
  52. {
  53. OLED_W_SDA(Byte & (0x80 >> i));
  54. OLED_W_SCL(1);
  55. OLED_W_SCL(0);
  56. }
  57. OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
  58. OLED_W_SCL(0);
  59. }
  60. /**
  61. * @brief OLED写命令
  62. * @param Command 要写入的命令
  63. * @retval 无
  64. */
  65. void OLED_WriteCommand(unsigned char Command)
  66. {
  67. OLED_I2C_Start();
  68. OLED_I2C_SendByte(0x78); //从机地址
  69. OLED_I2C_SendByte(0x00); //写命令
  70. OLED_I2C_SendByte(Command);
  71. OLED_I2C_Stop();
  72. }
  73. /**
  74. * @brief OLED写数据
  75. * @param Data 要写入的数据
  76. * @retval 无
  77. */
  78. void OLED_WriteData(unsigned char Data)
  79. {
  80. OLED_I2C_Start();
  81. OLED_I2C_SendByte(0x78); //从机地址
  82. OLED_I2C_SendByte(0x40); //写数据
  83. OLED_I2C_SendByte(Data);
  84. OLED_I2C_Stop();
  85. }
  86. /**
  87. * @brief OLED设置光标位置
  88. * @param Y 以左上角为原点,向下方向的坐标,范围:0~7
  89. * @param X 以左上角为原点,向右方向的坐标,范围:0~127
  90. * @retval 无
  91. */
  92. void OLED_SetCursor(unsigned char Y, unsigned char X)
  93. {
  94. OLED_WriteCommand(0xB0 | Y); //设置Y位置
  95. OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置低4位
  96. OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置高4位
  97. }
  98. /**
  99. * @brief OLED清屏
  100. * @param 无
  101. * @retval 无
  102. */
  103. void OLED_Clear(void)
  104. {
  105. unsigned char i, j;
  106. for (j = 0; j < 8; j++)
  107. {
  108. OLED_SetCursor(j, 0);
  109. for(i = 0; i < 128; i++)
  110. {
  111. OLED_WriteData(0x00);
  112. }
  113. }
  114. }
  115. /**
  116. * @brief OLED显示一个字符
  117. * @param Line 行位置,范围:1~4
  118. * @param Column 列位置,范围:1~16
  119. * @param Char 要显示的一个字符,范围:ASCII可见字符
  120. * @retval 无
  121. */
  122. void OLED_ShowChar(unsigned char Line, unsigned char Column, char Char)
  123. {
  124. unsigned char i;
  125. OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
  126. for (i = 0; i < 8; i++)
  127. {
  128. OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
  129. }
  130. OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
  131. for (i = 0; i < 8; i++)
  132. {
  133. OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
  134. }
  135. }
  136. /**
  137. * @brief OLED显示字符串
  138. * @param Line 起始行位置,范围:1~4
  139. * @param Column 起始列位置,范围:1~16
  140. * @param String 要显示的字符串,范围:ASCII可见字符
  141. * @retval 无
  142. */
  143. void OLED_ShowString(unsigned char Line, unsigned char Column, char *String)
  144. {
  145. unsigned char i;
  146. for (i = 0; String[i] != '\0'; i++)
  147. {
  148. OLED_ShowChar(Line, Column + i, String[i]);
  149. }
  150. }
  151. /**
  152. * @brief OLED次方函数
  153. * @retval 返回值等于X的Y次方
  154. */
  155. unsigned int OLED_Pow(unsigned int X, unsigned int Y)
  156. {
  157. unsigned int Result = 1;
  158. while (Y--)
  159. {
  160. Result *= X;
  161. }
  162. return Result;
  163. }
  164. /**
  165. * @brief OLED显示数字(十进制,正数)
  166. * @param Line 起始行位置,范围:1~4
  167. * @param Column 起始列位置,范围:1~16
  168. * @param Number 要显示的数字,范围:0~4294967295
  169. * @param Length 要显示数字的长度,范围:1~10
  170. * @retval 无
  171. */
  172. void OLED_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
  173. {
  174. unsigned char i;
  175. for (i = 0; i < Length; i++)
  176. {
  177. OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
  178. }
  179. }
  180. /**
  181. * @brief OLED显示数字(十进制,带符号数)
  182. * @param Line 起始行位置,范围:1~4
  183. * @param Column 起始列位置,范围:1~16
  184. * @param Number 要显示的数字,范围:-2147483648~2147483647
  185. * @param Length 要显示数字的长度,范围:1~10
  186. * @retval 无
  187. */
  188. void OLED_ShowSignedNum(unsigned char Line, unsigned char Column, signed int Number, unsigned char Length)
  189. {
  190. unsigned char i;
  191. unsigned int Number1;
  192. if (Number >= 0)
  193. {
  194. OLED_ShowChar(Line, Column, '+');
  195. Number1 = Number;
  196. }
  197. else
  198. {
  199. OLED_ShowChar(Line, Column, '-');
  200. Number1 = -Number;
  201. }
  202. for (i = 0; i < Length; i++)
  203. {
  204. OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
  205. }
  206. }
  207. /**
  208. * @brief OLED显示数字(十六进制,正数)
  209. * @param Line 起始行位置,范围:1~4
  210. * @param Column 起始列位置,范围:1~16
  211. * @param Number 要显示的数字,范围:0~0xFFFFFFFF
  212. * @param Length 要显示数字的长度,范围:1~8
  213. * @retval 无
  214. */
  215. void OLED_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
  216. {
  217. unsigned char i, SingleNumber;
  218. for (i = 0; i < Length; i++)
  219. {
  220. SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
  221. if (SingleNumber < 10)
  222. {
  223. OLED_ShowChar(Line, Column + i, SingleNumber + '0');
  224. }
  225. else
  226. {
  227. OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
  228. }
  229. }
  230. }
  231. /**
  232. * @brief OLED显示数字(二进制,正数)
  233. * @param Line 起始行位置,范围:1~4
  234. * @param Column 起始列位置,范围:1~16
  235. * @param Number 要显示的数字,范围:0~1111 1111 1111 1111
  236. * @param Length 要显示数字的长度,范围:1~16
  237. * @retval 无
  238. */
  239. void OLED_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
  240. {
  241. unsigned char i;
  242. for (i = 0; i < Length; i++)
  243. {
  244. OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
  245. }
  246. }
  247. /**
  248. * @brief OLED初始化
  249. * @param 无
  250. * @retval 无
  251. */
  252. void OLED_Init(void)
  253. {
  254. unsigned int i, j;
  255. for (i = 0; i < 1000; i++) //上电延时
  256. {
  257. for (j = 0; j < 1000; j++);
  258. }
  259. OLED_I2C_Init(); //端口初始化
  260. OLED_WriteCommand(0xAE); //关闭显示
  261. OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
  262. OLED_WriteCommand(0x80);
  263. OLED_WriteCommand(0xA8); //设置多路复用率
  264. OLED_WriteCommand(0x3F);
  265. OLED_WriteCommand(0xD3); //设置显示偏移
  266. OLED_WriteCommand(0x00);
  267. OLED_WriteCommand(0x40); //设置显示开始行
  268. OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
  269. OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
  270. OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
  271. OLED_WriteCommand(0x12);
  272. OLED_WriteCommand(0x81); //设置对比度控制
  273. OLED_WriteCommand(0xCF);
  274. OLED_WriteCommand(0xD9); //设置预充电周期
  275. OLED_WriteCommand(0xF1);
  276. OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
  277. OLED_WriteCommand(0x30);
  278. OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
  279. OLED_WriteCommand(0xA6); //设置正常/倒转显示
  280. OLED_WriteCommand(0x8D); //设置充电泵
  281. OLED_WriteCommand(0x14);
  282. OLED_WriteCommand(0xAF); //开启显示
  283. OLED_Clear(); //OLED清屏
  284. }
  285. // OLED_ShowCN(1+i*16,0,i);
  286. //显示汉字 (x:横坐标 y:纵坐标 N:字数 S:字号)
  287. void OLED_ShowCN(unsigned char x,unsigned char y,unsigned char N)
  288. {
  289. unsigned char wm=0;
  290. unsigned int addr;
  291. addr= 32*N;
  292. OLED_SetCursor(y,x); //设置起始光标,起始光标的位置时先y后x,
  293. for(wm=0;wm<16;wm++)
  294. {
  295. OLED_WriteData( font[addr]);
  296. addr +=1;
  297. }
  298. OLED_SetCursor(y+1,x);
  299. for(wm=0;wm<16;wm++)
  300. {
  301. OLED_WriteData( font[addr]);
  302. addr +=1;
  303. }
  304. }
  305. // Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
  306. // Description : 显示BMP位图
  307. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char S)
  308. {
  309. unsigned int j=0;
  310. unsigned char x,y;
  311. if(y1%8==0)
  312. y = y1/8;
  313. else
  314. y = y1/8 + 1;
  315. for(y=y0;y<y1;y++)
  316. {
  317. OLED_SetCursor(y,x0);
  318. for(x=x0;x<x1;x++)
  319. {
  320. switch(S)
  321. {
  322. case 1:OLED_WriteData(gImage_init101[j++]);break;
  323. case 2:OLED_WriteData(gImage_init102[j++]);break;
  324. case 3:OLED_WriteData(gImage_init103[j++]);break;
  325. case 4:OLED_WriteData(gImage_init104[j++]);break;
  326. case 5:OLED_WriteData(gImage_init105[j++]);break;
  327. case 6:OLED_WriteData(gImage_init106[j++]);break;
  328. case 7:OLED_WriteData(gImage_init107[j++]);break;
  329. case 8:OLED_WriteData(gImage_init108[j++]);break;
  330. case 9:OLED_WriteData(gImage_init109[j++]);break;
  331. case 10:OLED_WriteData(gImage_init110[j++]);break;
  332. case 11:OLED_WriteData(gImage_init111[j++]);break;
  333. case 12:OLED_WriteData(gImage_init112[j++]);break;
  334. case 13:OLED_WriteData(gImage_init113[j++]);break;
  335. case 14:OLED_WriteData(gImage_init114[j++]);break;
  336. case 15:OLED_WriteData(gImage_init115[j++]);break;
  337. case 16:OLED_WriteData(gImage_init116[j++]);break;
  338. case 17:OLED_WriteData(gImage_init117[j++]);break;
  339. case 18:OLED_WriteData(gImage_init118[j++]);break;
  340. case 19:OLED_WriteData(gImage_init119[j++]);break;
  341. case 20:OLED_WriteData(gImage_init120[j++]);break;
  342. case 21:OLED_WriteData(gImage_init121[j++]);break;
  343. case 22:OLED_WriteData(gImage_init122[j++]);break;
  344. case 23:OLED_WriteData(gImage_init123[j++]);break;
  345. case 24:OLED_WriteData(gImage_init124[j++]);break;
  346. case 25:OLED_WriteData(gImage_init125[j++]);break;
  347. }
  348. }
  349. }
  350. }
  351. void OLED_DrawBMP1(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char S)
  352. {
  353. unsigned int j=0;
  354. unsigned char x,y;
  355. if(y1%8==0)
  356. y = y1/8;
  357. else
  358. y = y1/8 + 1;
  359. for(y=y0;y<y1;y++)
  360. {
  361. OLED_SetCursor(y,x0);
  362. for(x=x0;x<x1;x++)
  363. {
  364. switch(S)
  365. {
  366. case 1:OLED_WriteData(gImage_IMG00000[j++]);break;
  367. case 2:OLED_WriteData(gImage_IMG00001[j++]);break;
  368. case 3:OLED_WriteData(gImage_IMG00002[j++]);break;
  369. case 4:OLED_WriteData(gImage_IMG00003[j++]);break;
  370. case 5:OLED_WriteData(gImage_IMG00004[j++]);break;
  371. case 6:OLED_WriteData(gImage_IMG00005[j++]);break;
  372. case 7:OLED_WriteData(gImage_IMG00006[j++]);break;
  373. case 8:OLED_WriteData(gImage_IMG00007[j++]);break;
  374. case 9:OLED_WriteData(gImage_IMG00008[j++]);break;
  375. case 10:OLED_WriteData(gImage_IMG00009[j++]);break;
  376. case 11:OLED_WriteData(gImage_IMG00010[j++]);break;
  377. case 12:OLED_WriteData(gImage_IMG00011[j++]);break;
  378. case 13:OLED_WriteData(gImage_IMG00012[j++]);break;
  379. case 14:OLED_WriteData(gImage_IMG00013[j++]);break;
  380. case 15:OLED_WriteData(gImage_IMG00014[j++]);break;
  381. case 16:OLED_WriteData(gImage_IMG00015[j++]);break;
  382. case 17:OLED_WriteData(gImage_IMG00016[j++]);break;
  383. case 18:OLED_WriteData(gImage_IMG00017[j++]);break;
  384. case 19:OLED_WriteData(gImage_IMG00018[j++]);break;
  385. case 20:OLED_WriteData(gImage_IMG00019[j++]);break;
  386. case 21:OLED_WriteData(gImage_IMG00020[j++]);break;
  387. case 22:OLED_WriteData(gImage_IMG00021[j++]);break;
  388. case 23:OLED_WriteData(gImage_IMG00022[j++]);break;
  389. case 24:OLED_WriteData(gImage_IMG00023[j++]);break;
  390. case 25:OLED_WriteData(gImage_IMG00024[j++]);break;
  391. case 26:OLED_WriteData(gImage_IMG00025[j++]);break;
  392. case 27:OLED_WriteData(gImage_IMG00026[j++]);break;
  393. case 28:OLED_WriteData(gImage_IMG00027[j++]);break;
  394. case 29:OLED_WriteData(gImage_IMG00028[j++]);break;
  395. case 30:OLED_WriteData(gImage_IMG00029[j++]);break;
  396. case 31:OLED_WriteData(gImage_IMG00030[j++]);break;
  397. case 32:OLED_WriteData(gImage_IMG00031[j++]);break;
  398. case 33:OLED_WriteData(gImage_IMG00032[j++]);break;
  399. case 34:OLED_WriteData(gImage_IMG00033[j++]);break;
  400. case 35:OLED_WriteData(gImage_IMG00034[j++]);break;
  401. case 36:OLED_WriteData(gImage_IMG00035[j++]);break;
  402. }
  403. }
  404. }
  405. }

OLED.H文件、

  1. #ifndef __OLED_H
  2. #define __OLED_H
  3. void OLED_Init(void);
  4. void OLED_Clear(void);
  5. void OLED_ShowChar(unsigned char Line, unsigned char Column, char Char);
  6. void OLED_ShowString(unsigned char Line, unsigned char Column, char *String);
  7. void OLED_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
  8. void OLED_ShowSignedNum(unsigned char Line, unsigned char Column, signed int Number, unsigned char Length);
  9. void OLED_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
  10. void OLED_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
  11. void OLED_ShowCN(unsigned char x,unsigned char y,unsigned char N);
  12. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char S);
  13. void OLED_DrawBMP1(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char S);
  14. #endif

 

字库文件太长了,不方便分享需要的话可以评论区私信我。

4. 完整的工程代码

这是本次的完整的工程代码文件,有需要的自取;

链接:https://pan.baidu.com/s/1jGXHAkab3jjR_Rd2WsBPcg?pwd=h8d5 
提取码:h8d5

第一次发文如果有不当的地方,欢迎指正。

码字不易,点个赞吧 =.=

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/553513
推荐阅读
相关标签
  

闽ICP备14008679号