ESP8266驱动初步学习
前言
刚刚从嘉立创开源的EDA改了一个Linux的开发核心板,嘉立创每个月免费打板两份,作为刚刚开始接触画板子的小白这样的好事怎么能错过,嵌入式打板真的成本不低,嘉立创EDA使用不够熟练(平时使用的是AD,但是AD掌握的也不是十分熟练,所以嘉立创更是接触很慢),只能选择一个开源的板子回来修改一下原理图打个板子回来试试,希望不会翻车。
第一步 了解熟悉ESP8266模块
在这段时间的了解(五六天的调试研究),这里就不引用网络上对这个模块的介绍了,只讲我自己的对这个模块的了解和认识。
据以前的了解,这个应该是乐鑫家的WiFi模块,还有一个集合WiFi和蓝牙功能的模块叫ESP8266,官方在其中应该烧录了SDK(固件),我们可以通过串口发送相关的命令来驱动模块实现想要的功能,我们可以使用USB转ttl模块连接ESP8266的串口引脚来发送命令,也可以使用单片机等类似的MCU发送指令实现相应的功能。
第二部 认识AT命令
简单使用的命令
ESP8266使用的是AT命令,这个命令什么意思不清楚,现在只管能驱动能使用实现我需要的功能就行,在本次驱动的时候只是用如下的命令,其他的命令使用方法相同,更多的命令就百度搜集吧,这里只介绍记录使用过程和调试遇见的bug。
这里参考了另一位博主写的文章,我在学习的过程中也在使用他发出来的网络接口和get地址,引用链接会挂在最底部,如有侵权请联系删除。
AT
AT+RST
AT+CWMODE=1
AT+CIPMUX=0
AT+CWJAP="你的WiFi名称","你的WiFi密码"
AT+CIPMODE=1
AT+CIPSTART="TCP","api.seniverse.com",80
AT+CIPSEND
GET https://api.seniverse.com/v3/weather/now.json?key=SwLQ3i0Q5TNa6NSKT&location=hangzhou&language=en&unit=c
+++
这些命令是本次使用到的命令
串口调试
使用USB转TTL连接ESP8266的串口(记得TX和RX反接),电脑端打开串口调试助手,连接ESP8266的端口,发送AT命令。记得设置波特率为115200。
这里发送AT命令时要发送"AT\r\n",要发送的时AT回车,或者在串口调试助手内选择发送新行,发送命令,但是上面第十行的+++不要带换行符,这个后面在讲。
因为我单片机已经烧录好了程序,不想再换了,这里只进行简单的介绍,发送AT\r\n
后,ESP8266会返回如下反馈。
AT
OK
对,返回的数据可以理解为AT换行换行OK
他会把你发出的指令返回,并带一个他的状态反馈。记住这里返回值的两个回车,后面在写程序的时候会注意到。
相关其他的命令也是这样的使用方法,勾选串口调试助手内的发送新行,发送+++
和get…
相关的命令时需要取消发送新行,发送后会受到ESP8266返回的状态反馈。
发送请求和退出透传
这里AT+CIPSEND
发送后返回的反馈是>
这样一个符号,AT+CIPSEND
的意思大概是可以发送你想发送的字符串到上一步连接到的网络端口。出现>
代表可以输入,这时输入get…
相关的语句就可以被接口识别并返回你想要的数据,此时我们处于的是一个叫透传的模式,如果不需要继续获取这个接口的数据或者想获取其他接口的数据我们需要退出透传模式,这里就要发送+++
,注意这里是没有换行符的,而且有可能一次发送不能被识别,可能需要多发送几次。
## 第三步 使用STM32驱动ESP8266
简单介绍思路
上面介绍了ESP8266的使用方法,和一部分的AT指令使用,我们是通过电脑的串口助手发送指令到ESP8266上的,而单片机也有串口,我们可以使用单片机上的串口发送相同的指令实现相同的功能,其他的MCU控制方法相同,只需要在串口发送相应的指令就可以获取它的反馈。
对ESP8266和ESP32模块有一定了解的小伙伴应该都知道这两个芯片是可以当作单片机使用的,就是说我们可以自己写程序进去,可以做自己想要的固件功能,我之前在调试的时候想用手里另一块ESP32来自己写固件发送自己想要的协议,迫于对模块的不了解和对python语言的不熟练没能实现全部功能,但是成功实现了串口的发送和接受,初步实现了两个芯片的通讯,有精力的小伙伴可以自己研究开发自己想要的固件,减少单片机内的程序量,而且python写程序真的好简洁,真的比c简单好多。
硬件连接
我们可以使用stm32的串口连接ESP8266的串口,当然是哪个串口都可以,配置好串口初始化,配置好发送的命令和相关的引脚,我的硬件是自己画的一个板子,复位和使能引脚提前被接好了,如果是买的开发板可以把相应的引脚接在单片机上通过程序控制使能和复位。
这里关于复位和使能的就不过多介绍了。
这里把ESP8266的串口和单片机的串口按照串口的规则连接,我连接到了串口三上,串口一接了CH340,串口二接了rs485,8266这里就被排到了串口三。
软件编写
配置好单片机的串口三初始化后编写相关的初始化函数:
.c文件
#include "./BSP/ESP8266/esp8266.h"
#include "string.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/usart3/usart3.h"
//#include "stm32f10x.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include <stdio.h>
/**************************************************************************/
//函数作用:ESP8266_Init初始化函数
//函数名称:ESP8266_Init(void);
//内部参数:
//修改日期:2022年4月18日 下午16:18
//作者: 大屁桃
/**************************************************************************/
void ESP8266_Init(void)
{
ESP8266_Clear();
/*让WIFI推出透传模式*/
while(ESP8266_SendCmd("+++", ""))
Exti_Tc();
delay_ms(100);
//printf("ESP8266_SendCmd: AT\r\n");
//测试ESP8266响应
while(ESP8266_SendCmd("AT\r\n", "OK"))
printf("ESP8266_SendCmd: AT OK\r\n");
delay_ms(100);
//ESP8266复位操作
ESP8266_SendCmd("AT+RST\r\n", "");
printf("ESP8266_SendCmd: AT+RST\r\n");
delay_ms(1000);
//
ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
printf("ESP8266_SendCmd: AT+CIPCLOSE\r\n");
delay_ms(100);
//
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
printf("ESP8266_SendCmd: AT+CWMODE=1\r\n");
delay_ms(100);
//
while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"))
printf("ESP8266_SendCmd: AT+CIPMUX=0\r\n");
delay_ms(500);
//连接wifi
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"))
printf("ESP8266_SendCmd: 连接WiFi\r\n");
delay_ms(100);
USART3_Send_String("esp8266_init_ok");
}
/***********************************************************
* 函数名称:Exit_Tc()
* 函数作用:退出透传模式
* 功能实现:发送+++后发送AT,检测是否返回OK
* 返回值: 1 已退出透传模式
* 0 退出失败
************************************************************/
void Exti_Tc()
{
int tc = 1;
int timeout=20;
char * res = "OK";
char * cmd = "AT\r\n";
while(tc++)
{
while(ESP8266_SendCmd("+++", ""));
while(ESP8266_SendCmd("+++", ""));
while(ESP8266_SendCmd("+++", ""));
while(ESP8266_SendCmd("+++", ""));
ESP8266_Sendstring( (char *)cmd);
while(timeout--)
{
if(strstr((char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
printf("已退出透传模式...");
ESP8266_Clear(); //清空缓存
//return 1;
tc=0;
}
delay_ms(10);
}
}
delay_ms(10);
}
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
//==========================================================
// 函数名称: 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));
ESP8266_Sendstring( (char *)cmd);
while(timeOut--)
{
//if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
//{
//if(*res != 0)
//{
if(strstr((char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
//USART3_Send_String((char *)g_usart3_rx_buf);
printf("%s",g_usart3_rx_buf);
ESP8266_Clear(); //清空缓存
return 0;
}
//}
//}
delay_ms(10);
}
return 1;
}
//==========================================================
// 函数名称: ESP8266_SendData
//
// 函数功能: 发送数据
//
// 入口参数: data:数据
// len:长度
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_SendData( char *data)//unsigned
{
char cmdBuf[32];
ESP8266_Clear(); //清空接收缓存
sprintf(cmdBuf, "AT+CIPSEND\r\n"); //发送命令
if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
{
//UsartPrintf(USART_DEBUG, "8.AT+CIPSEND\r\n");
/*发送请求数据*/
//Usart_SendString(USART2, data, len); //发送设备连接请求数据
//delay_ms(100);
USART3_Send_String( (char *)data);
}
}
这里是参考来的程序,并做了自己想要的修改,我想要的只是对ESP8266驱动的开发,所以把其他的内容删掉,自己添加了一些新的内容进去。
.h文件
#ifndef __ESP8266_H
#define __ESP8266_H
#include "./SYSTEM/usart3/usart3.h"
#define REV_OK 1
#define REV_WAIT 0
#define esp8266_buf g_usart3_rx_buf //串口接收缓冲区
#define ESP8266_Sendstring USART3_Send_String //串口3发送
#define esp8266_cnt g_usart3_rx_sta //接收状态标志位(软件定义的寄存器)
#define ESP8266_WIFI_INFO "AT+CWJAP=\"wifi名\",\"wifi密码\"\r\n" //定义wifi账号密码
//WIFI和密码·
void ESP8266_Clear(void);
_Bool ESP8266_SendCmd(char *cmd, char *res);
void ESP8266_SendData( char *data);
void ESP8266_Init(void);
void Exti_Tc(void);
#endif
这些实现后就可以完成对ESP8266的连接WiFi网络的初始化了,也达到了我编写驱动的目的。
最后整理 途中遇见的bug
啊,想想就心酸,在我起初打算了解这个模块的时候真的觉得很简单,他的操作过程和使用方法都超级简单,但是在我编写整个单片机程序的时候引文经验不足且使用不规范留下了太多的bug,以至于一个驱动搞了近一个星期,期间芯片还坏了一次,买的新的芯片焊接。
之前在使用串口一发送串口三接收的8266返回数据的时候总会遇到乱码的情况,本以为是电路或者程序的问题,可是每当程序刚刚下载进去的时候串口一读取的数据是正常的,整块板子重新上电之后打印出的就是乱码,分析好久找不到原因。结果第二天……芯片写不进去程序了,芯片内原有的程序也不运行了,得,芯片坏了,下单买新的芯片吧,今天才到,焊接上才把程序调通。
在串口调试通讯过程中一直遇到发送和打印的各种问题……啊这啊这,都是我自己的不注意,天啊,串口三接收的数据本想串口一打印,结果用了串口三发送的函数,结果就是单片机和8266不停的在通讯,还有就是单片机发送数据时没有加换行符,头大。
再有一点,我最初使用的是正点原子寄存器般的串口接收中断服务函数,起初没有对这个函数过多了解,只是能用就可以了,但是这段时间调试遇到了好多bug,经常有返回不全等问题,这里贴出正点原子寄存器版的串口终端服务函数:
void USART_UX_IRQHandler(void)
{
uint8_t rxdata;
#if SYS_SUPPORT_OS /* 如果SYS_SUPPORT_OS为真,则需要支持OS. */
OSIntEnter();
#endif
if (USART_UX->SR & (1 << 5)) /* 接收到数据 */
{
rxdata = USART_UX->DR;
if ((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成? */
{
if (g_usart_rx_sta & 0x4000) /* 接收到了0x0d? */
{
if (rxdata != 0x0a) /* 接收到了0x0a? (必须先接收到到0x0d,才检查0x0a) */
{
g_usart_rx_sta = 0; /* 接收错误, 重新开始 */
}
else
{
g_usart_rx_sta |= 0x8000; /* 收到了0x0a,标记接收完成了 */
}
}
else /* 还没收到0x0d */
{
if (rxdata == 0x0d)
{
g_usart_rx_sta |= 0x4000; /* 标记接收到了 0x0d */
}
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = rxdata; /* 存储数据到 g_usart_rx_buf */
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))g_usart_rx_sta = 0;/* 接收数据溢出, 重新开始接收 */
}
}
}
}
#if SYS_SUPPORT_OS /* 如果SYS_SUPPORT_OS为真,则需要支持OS. */
OSIntExit();
#endif
}
#endif
在调试程序过程中我一度观察串口的DR寄存器数据,经常有看见0x0D的数据,但是一直没太在意,直到有一天数据接收总是不全,我又在考虑串口接收的时候是根据什么标志为结尾来停止接受的,想了想看了看源码,这怎么有个0x0D挺眼熟的,百度一搜,好家伙,换行符,这不巧了么,上面说过,8266返回的数据是带有换行符的,而正点原子这个串口接收中断服务函数是识别换行符停止接收数据的,这就明白为什么接收数据总是不全了,我还一度再修改缓冲区大小,查找中断标志位,查看寄存器,查看接收,结果……不过也好,学了好多的知识,收获不小。
在这期间也了解了一下关于单片机实现printf
语句的方法,他是重写了printf
的底层语句,printf
是通过执行fputc
函数来实现打印的,正点原子提供的printf
实现方法就是重写了fputc
函数,使printf
打印的数据使用串口的DR
寄存器发送,也就是实现了串口的打印,本来想实现多个串口的printf
功能的,后来发现不如自己写一个类printf
来打印,结果发现有send_string
函数,那就好了,不需要自己写了,修修补补移植过来,ok,适配成功。
参考链接
STM32驱动ESP8266WiFi模块获取天气