51单片机+ESP8266-01通过串口通信控制LED灯

前期准备

硬件:51单片机开发板、ESP8266-01WIFI模块、USB-TTL转串口;
软件:Keil uv4单片机编程软件、串口调试助手XCOM V2.0、有人网络助手;
基础知识:单片机串口通信基础 、ESP8266AT指令集;

配置并测试ESP8266

1. ESP8266与USB-TTL转串口连接

ESP8266管脚 VCC CH_PD/EN TX RX GND
USB-TTL管脚 3.3V 3.3V RXD TXD GND

       连线完成后,插入电脑USB口准备通过串口调试助手向ESP8266发送AT指令进行配置和测试。

2. 通过串口调试助手向ESP8266发送AT指令

       初次使用的ESP8266模块默认在AP模式(Access Point接入点),即由ESP8266开启热点,供其他设备接入形成局域网。上电成功可搜索到模块的WIFI信号,如AI-THINKER_XXXX,表明模块正常工作。
       打开串口调试助手,选择串口、设置波特率,ESP8266默认波特率为115200,因此设置波特率为 115200,打开串口,在输入框中输入AT,然后回车再点击发送。如果串口调试助手配置正确,可在接收框收到ESP8266的回传信息“OK”,若显示许多字母数字组合的16进制数,如54 0D等等,需要将串口助手的16进制显示取消勾选。回传"OK"证明ESP8266配置成功。
       进一步配置ESP8266工作在AP模式,作为服务器供其他设备接入实现通信,依次在输入框中发送AT指令:
       AT+CWMODE=2
       配置ESP8266模块为AP模式,开启WiFi热点;
       AT+CWSAP=’“WiFi名字”,“WiFi密码”,11,3
       更改WiFi名称,设置WiFi密码,如AI-THINKER_8266,123456789;
       AT+RST
       配置完成AP工作模式后需要重启使配置生效;
       AT+CIPMUX=1
       启动多连接,ESP8266作为服务器最多支持5个客户端连接;
       AT+CIPSERVER=1,8080
       模块开启服务器模式,端口号8080,可自行修改;
       AT+CIFSR
       查询ESP8266IP,默认为192.168.4.1,便于客户端连接服务器时使用;
       AT+CIOBAUD=4800
       设置ESP8266波特率为4800(可自行修改)为后续与单片机通信做准备,必须与单片机串口通信波特率相一致,设置波特率之后也需要修改电脑串口助手的波特率由115200改成4800才可以继续发送AT指令。
       注:前两条指令设置完成后,断电复位后配置不变只需要进行一次配置。红色指令需要在每次断电或重启之后重新配置,最后两条指令分别为网络连接、单片机串口通信做准备。

3. 通过网络调试助手与ESP8266服务器进行通信

       完成上述配置后,通过手机输入密码连接刚刚配置好的WiFi,打开手机端有人网络助手。

       点击tcp-server——配置——输入端口号8080——激活
       端口激活成功显示: 服务开启端口:8080 服务器IP:xxx.xxx.x.x

       点击tcp-client——增加——输入IP地址:192.168.4.1;端口:8080——增加
       连接打开成功显示:192.168.4.1 port:8080 ,若显示disconnect,可尝试使用串口调试助手发送重启和ESP8266重启输入两条红色AT指令进行重新配置。

       连接成功后可通过手机端有人网络调试助手发送框向ESP8266服务器发送信息如:123,点击发送。
       发送成功后,电脑端的串口调试助手会接收到ESP8266回传的信息:+IPD,0,3:123可以知道回传信息格式:+IPD,N,X:Y 其中N表示客户端编号(0~4),X表示数据长度,Y表示传送的数据。

配置并调试51单片机+ESP8266

1. ESP8266与51单片机连接

ESP8266管脚 VCC CH_PD/EN TX RX GND
51单片机 3.3V 3.3V RXD(P3^0) TXD(P3^1) GND

       接线方式与USB-TTL转串口一致,注意51单片机串口通信RXD为P30管脚,TXD为P31管脚,供电采用3.3V电压,5V可能导致ESP8266过热烧毁。开发板上没有3.3V供电方式的可自行搭建电源电路,若开发板有USB或PS2接口可将USB-TTL转串口插入获得3.3V电源。

2. ESP8266与51单片机串口通信实现思路

       基于通过电脑串口调试助手配置ESP8266的经历可以知道,要实现两者之间正确通信,需要设置相同的波特率。ESP8266掉电重启后需要再次配置,因此需要由单片机发送AT+CIPMUX=1,AT+CIPSERVER=1,8080进行配置,接收回传“OK”或“ERROR” ,为方便代码调试可对回传信息进行判断控制LED显示配置是否成功。配置成功之后由手机端向ESP8266发送数据,经过串口通信51单片机会接收到+IPD,N,X:Y,只需要将数据Y进行存储与判断,即可实现手机无线控制,如控制LED的亮灭。

   初始化配置51单片机

void Usart_Init()
{
	SCON = 0x50;	//设置串行口工作在方式一,且启动串行口接收(REN=1)
	TMOD = 0x20;	//设置计数器1工作在方式二,即自动重装计数值(8位)
	
	TH1 = 0xf3;		//设置波特率为4800
	TL1 = 0xf3;
	PCON=0X80;    
	  
	TR1 = 1; 	    //启动计数器
	EA = 1;         //开总中断
}	

       初始化程序主要是对51单片机定时器、中断相关的寄存器进行配置,不明白的可以学习单片机串口通信的相关内容。设置定时器的初值配置波特率为4800,ESP8266与单片机通信时需要先通过电脑串口调试助手设置波特率,这也是上文在AT指令部分设置波特率的原因。

   通过51单片机配置ESP8266

void WIFI_Init()
{
	ES = 0;						      //关闭串口中断
	TI = 1; 						  //使用printf串口输出之前TI置1
	printf("AT+RST\r\n");			  //AT指令后加\r\n表示回车换行
	ms_delay(1000) ;				  //毫秒级延时	  
	printf("AT+CIPMUX=1\r\n");
	ms_delay(1000) ;
	printf("AT+CIPSERVER=1,8080\r\n");        
	ms_delay(1000) ;
	while(!TI);                      //等待发送完成
	TI = 0;							 //发送标志位清零
	ES = 1;							 //打开串口中断
}

       通过51单片机串口通信中断传送的方式向ESP8266发送AT指令进行配置,首先关闭串口中断,避免发送AT指令的过程被其他串口中断过程打扰。借助printf()函数可以很方便的向窗口发送数据, printf函数是调用putchar进行数据传送的,而putchar先判断TI是否为1,TI为1则清零并开始发送,不为1则等待为1,因此TI需要提前置1。while(!TI)相当于while(TI==0),表示等待发送结束,TI自动置1跳出,然后软件清零,开串口中断完成ESP8266初始化。

   代码实现AT指令发送及回传信息简单判断

       为了加深对串口收发数据的理解,我们不借助printf函数自己实现AT指令的发送,数据产生方式有中断传送方式和查询传送方式,通过RI和TI置1触发中断,1个8位寄存器SBUF作为数据缓存,**一次传送或接收一个字节(8bit)**满足条件便逐个字节收发数据的中断传送数据更像是一个潜在的循环,虽然传输效率更高,但初学不容易理解,可以借助查询传送的方式定义一个标志位,通过软件控制数据的收发。

void Sent_AT(char *At_Comd)  //定义字符串指针指向AT指令
{
	ES = 0;
	while(*At_Comd != '\0')   //字符串结束标志
	{
		SBUF = *At_Comd;	  //写入1个字节数据
		while(!TI);			  //等待该字节数据发送完成
		TI = 0;				  //发送标志位清零
		us_delay(5);          //微秒级延时
		At_Comd++;			  //指向下一个字节
	}
}

void WIFI_Init()
{
        char TX_Flag == 1;					   //发送状态标志位,1允许发送,0发送结束
		char RX_Flag == 1;					   //接收状态标志位,1允许接收,0接收结束
		char num=0;							   //接收超时判断
		char Recivedata;					   //存储接收到的一个字节数据
        ES = 0;                                //关闭串口中断,防止收发过程被打扰
		while(TX_Flag == 1){
			Sent_AT("AT+CIPMUX=1\r\n");        //调用发送函数
			//LED1=0;						   //表示发送正常
				while(RX_Flag == 1)            //检测模块是否返回OK
				{
					if(RI)
					{
						RI = 0;
						Recivedata = SBUF;	   //保存当前接收到的1个字
						if(Recivedata == 'K')  //回传“OK”中的K表示配置完成
						{
							TX_Flag = 0;	   //标志位清零,跳出收发循环
							RX_Flag = 0;
						}
					}
					num++;					   //累加
					if(num == 1000)			   //未收到OK或接收超时
					{
						num = 0;
						RX_Flag = 0;		   //跳出接收查询,再次发送
					}
				}
			}
		TX_Flag = 1;						   //跳出循环,表示配置成功
		RX_Flag = 1;
		num = 0;							   //标志位还原
    //  LED2 = 0;								   //可通过LED灯表示配置完成
			
		while(TX_Flag == 1){
				Sent_AT("AT+CIPSERVER=1,8080\r\n");
			//  LED3=0;
				while(RX_Flag == 1)
				{
					if(RI)
					{
						RI = 0;
						Recivedata = SBUF;
						if(Recivedata == 'K')
						{
							TX_Flag= 0;
							RX_Flag = 0;
						}
					}
					num++;
					if(num == 1000)
					{
						num = 0;
						RX_Flag = 0;
					}
				}
			}
		TX_Flag= 1;
		RX_Flag = 1;
		num = 0;
		ES=1;
	//	LED4 = 0;			
}

       上述两个子函数实现了AT指令的发送以及简单的回传信息判断,利用TX_Flag和RX_Flag两个标志位循环嵌套,可以发现只有当发送AT指令并回传收到OK时才跳出循环,未收到OK或接收超时跳出接收循环再次发送AT指令。跳出循环即配置完成,可以通过LED灯显示代码运行情况。上述程序同样也是对ESP8266进行配置与调用printf的函数作用相同。

   中断服务程序收并处理数据

       在中断服务程序中对客户端发送并由ESP8266回传的+IPD,N,X:Y格式的数据进行处理,首先我们需要定义一个数组来存储接收到的每一个字节数据。考虑到可能要在main函数中对数组的数据进行处理,最好定义为全局变量。设定发送“1”点亮LED、“0”熄灭。这样回传的数据如“+IPD,0,1:1”仅需要10个字节长度的数组就可以保存回传的信息,在下一次接收前数组归零(i=0)。对数组下标为9的字节数据进行提取,完成相应的操作。

char Recive_table[15];					//定义全局变量
int i;

void Uart_Interrupt() interrupt 4                //中断服务程序接收并处理数据
{
		if(RI==1)
		{
			RI=0;					   //接收标志位清零
			Recive_table[i] = SBUF;	   //将数据存入数组
			if(Recive_table[0]=='+')   //初步检测有效信息
		    {i++;}      
			else
			{i=0;}
			if(i>=10)  				   //一组数据接收完成
			{	
			//	LED5 = 0;				   //LED显示接收完成
				if((Recive_table[0]=='+')&&(Recive_table[1]=='I')&&(Recive_table[2]=='P')&&(Recive_table[3]=='D'))
				{									 //检测有效信息
					if((Recive_table[9]=='0'))
					{
						LED8=0;
					}else if((Recive_table[9]=='1'))
					{
						LED8=1;
					}
				//	LED7=0;							 //发送信息不符合
					i = 0;
				}
			// 	LED6=0;								 //未接收到正确格式数据
				i = 0;
			}
		}
		else TI = 0;
}

       上述为中断服务程序,即处理器响应中断后要完成的操作,串口中断以数据发送或者接受的TI和RI 置1作为触发条件,只要存在数据收发就会进入该中断服务程序,这可能也是前面在发送AT指令等操作是为什么要关闭串口中断的原因。在主函数中调用单片机初始化程序void Usart_Init(),ESP8266初始化程序void WIFI_Init(void),然后while(1),让处理器一直循环等待中断。

       注:向单片机烧录程序的时候拔掉与ESP8266连接的RX端和TX端,即空出P30,P31管脚。

完整程序

       许多博客和单片机论坛里关于51单片机+ESP8266的教程大同小异,许多朋友拿到例程,在测试的过程中无法正常通信,又不知道问题出在哪里。我想的表达的是,我们可以通过增加代码的复杂度,如多一些条件语句配合LED灯进行显示,一步步的完成配置和调试。本人也是初学单片机,文章综合了许多博客和论坛的帖子,希望对大家有所帮助。

#include 
#include 
#include 
#define uchar unsigned char 		   //对数据类型进行声明定义
#define uint unsigned int 

sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;						   //用于调试的LED灯
sbit LED5 = P2^4;
sbit LED6 = P2^5;
sbit LED7 = P2^6;
sbit LED8 = P2^7;						   //手机可以控制的LED灯

uchar TX_Flag = 1;				   //发送状态标志位,1允许发送,0发送结束
uchar RX_Flag = 1;					   //接收状态标志位,1允许接收,0接收结束
uchar num=0;						   //接收超时判断
uchar Recivedata;					   //存储接收到的一个字节数据

uchar Recive_table[15];
uint i=0;

void ms_delay(uint n)	
{
	uint j,m;
	for( m =n; m>0; m--)
	for(j=110; j>0; j--);
}
void us_delay(uchar n)
{
	while(n--);
}
/******************************************************************
函 数: void Usart_init()
功 能: 单片机初始化
参 数: 无
返回值: 无
*******************************************************************/
void Usart_init()
{
	SCON = 0x50;	//设置串行口工作在方式一,且启动串行口接收(REN=1)
	TMOD = 0x20;	//设置计数器1工作在方式二,即自动重装计数值(8位)
	PCON=0X80;
	TH1 = 0xf3;		//设置波特率为4800
	TL1 = 0xf3;
	
	EA = 1;       //开总中断
	TR1 = 1; 			//启动计数器
}
/******************************************************************
函 数: void Sent_AT(uchar *At_Comd)
功 能: AT指令发送
参 数: 字符串指针
返回值: 无
*******************************************************************/
void Sent_AT(uchar *At_Comd)  //定义字符串指针指向AT指令
{
	ES = 0;
	while(*At_Comd != '\0')   //字符串结束标志
	{
		SBUF = *At_Comd;	  //写入1个字节数据
		while(!TI);			  //等待该字节数据发送完成
		TI = 0;				  //发送标志位清零
		us_delay(5);
		At_Comd++;			  //指向下一个字节
	}
}
/******************************************************************
函 数: void WIFI_Init(void)
功 能: wifi初始化
参 数: 无
返回值: 无
*******************************************************************/
void WIFI_Init()
{

        ES = 0;                                //关闭串口中断,防止收发过程被打扰
		while(TX_Flag == 1){
			Sent_AT("AT+CIPMUX=1\r\n");        //调用发送函数
			  LED1=0;						   //表示发送正常
				while(RX_Flag == 1)            //检测模块是否返回OK
				{
					if(RI)
					{
						RI = 0;
						Recivedata = SBUF;	   //保存当前接收到的1个字
						if(Recivedata == 'K')  //回传“OK”中的K表示配置完成
						{
							TX_Flag = 0;	   //标志位清零,跳出收发循环
							RX_Flag = 0;
						}
					}
					num++;					   //累加
					if(num == 1000)			   //未收到OK或接收超时
					{
						num = 0;
						RX_Flag = 0;		   //跳出接收查询,再次发送
					}
				}
			}
		TX_Flag = 1;						   //跳出循环,表示配置成功
		RX_Flag = 1;
		num = 0;							   //标志位还原
        LED2 = 0;							   //可通过LED灯表示配置完成
			
		while(TX_Flag == 1){
				Sent_AT("AT+CIPSERVER=1,8080\r\n");
			    LED3=0;
				while(RX_Flag == 1)
				{
					if(RI)
					{
						RI = 0;
						Recivedata = SBUF;
						if(Recivedata == 'K')
						{
							TX_Flag= 0;
							RX_Flag = 0;
						}
					}
					num++;
					if(num == 1000)
					{
						num = 0;
						RX_Flag = 0;
					}
				}
			}
		TX_Flag= 1;
		RX_Flag = 1;
		num = 0;
		LED4 = 0;
		ES=1;			
}
	/**********************主函数************************/
void main()
{
		Usart_init();
		ms_delay(5000);
		WIFI_Init();
		while(1);				///循环等待中断
}

/******************************************************************
函 数: void Uart_Interrupt() interrupt 4
功 能: 串口中断函数,将收到的字符存到Recive_table[]数组中
参 数: 无
返回值: 无
*******************************************************************/
void Uart_Interrupt() interrupt 4                //中断服务程序接收并处理数据
{
		if(RI==1)
		{
			RI=0;					   //接收标志位清零
			Recive_table[i] = SBUF;	   //将数据存入数组
			if(Recive_table[0]=='+')   //初步检测有用信息
			{i++;}      
			else
			{i=0;}
			if(i>=10)  				   //一组数据接收完成
			{	
				LED5 = 0;				   //LED显示接收完成
				if((Recive_table[0]=='+')&&(Recive_table[1]=='I')&&(Recive_table[2]=='P')&&(Recive_table[3]=='D'))
				{									 //检测有效信息
					if((Recive_table[9]=='0'))
					{
						LED8=0;
					}else if((Recive_table[9]=='1'))
					{
						LED8=1;
					}
					LED7=0;							 //发送信息不符合
					i = 0;
				}
			 	LED6=0;								 //未接收到正确格式数据
				i = 0;
			}
		}
		else TI = 0;
}

你可能感兴趣的:(51单片机+ESP8266-01通过串口通信控制LED灯)