09:串口通信四(蓝牙模块和WiFi模块的使用)

蓝牙模块和WiFi模块的使用

1、蓝牙模块的使用

  将蓝牙模块的TX和RX分别连接到单片机的RX和TX引脚上面。
在这里插入图片描述
我们写一个程序:手机通过蓝牙模块控制单片机的LED1的亮灭

#include <REGX52.H>
#include "intrins.h"
#include <string.h>

sfr AUXR = 0X8E;
sbit LED1 = P3^7;
char dma_js[5];//定义5个字节的字符数据来存储上位机发送来的字符串
char dma_fs[] = "wohaoshuai\r\n";

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x40;		//8位数据,可变波特率
	REN = 1;
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	TR1 = 1;		//启动定时器1
	
	ES = 1;//开启串口中断
	EA = 1;//开启中断总开关
}


void sendByte(char data_sj)
{
	SBUF = data_sj;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void main()
{
	LED1 = 1;
	UartInit();
	while(1)
	{
		Delay300ms();
		sendString(dma_fs);
		Delay1000ms();
	}
}
/*
			中断函数中让控制LED1状态的方法1:
*/
//void UART_Handler() interrupt 4
//{
//	static int i = 0;
//		if(RI == 1)
//		{
//			RI = 0;
//			dma_js[i]= SBUF;
//			i++;
//			if(i > 5)//如果i等于6,代表数组已经存满了,
//			i = 0;
//			if(dma_js[0] == 'o'&& dma_js[3] == 'n')//输入open
//			{
//				LED1 = 0;
//				i = 0;
//				memset(dma_js,'\0',5);//清空字符数组里面的字符串
//			}	
//			if(dma_js[0] == 'c'&& dma_js[4] == 'e')//输入close
//			{
//				LED1 = 1;
//				i = 0;
//				memset(dma_js,'\0',5);
//			}	
//		}
//}

/*
			中断函数中让控制LED1状态的方法2:
*/
//void UART_Handler() interrupt 4
//{
//	static int i = 0;
//  char mark;
//		if(RI == 1)
//		{
//			RI = 0;
//			mark = SBUF;
//			if(mark == 'o'|| mark == 'c')//如果发送来的是字符串中有'o'或者有'c'
//			{
//					i = 0;
//			}
//			dma_js[i] = mark;//则把o/c存放在数组dma_js的第一位
//			i++;             //在o/c之后的字符依次存放。
//			if(i == 5)//字符串超过了数组,超过的部分依次从第一位开始存放
//			{
//				i = 0;
//			}
//			if(dma_js[0]=='o' && dma_js[3] == 'n')//open开灯
//			{
//					LED1 = 0;	
//				  i = 0;
//				memset(dma_js,'\0',5);
//			}
//			if(dma_js[0]=='c' && dma_js[4] == 'e')//close开灯
//			{
//					LED1 = 1;
//				   i = 0;
//				memset(dma_js,'\0',5);			
//			}
//		}			
//}
/*
	方法2不太好,因为open和close中都存在字符‘o’,当发送字符串close时,我本来想关灯的,但是程序执行时,
	先检测到字符‘c’,所以数组依次存放'c','l',但是第3个字符是‘o’,它也满足判断条件,所以把‘o’存放在
	数组的第一位把c覆盖了,然后后面的字符依次存放。所以如果输入的字符串是close,则存放在数组中的是ose。
	所以我们把close里面的判断改为dma_js[0]=='o' && dma_js[2] == 'e'。但是这样又存在问题了,如果输入
	open时。它也满足这个判断条件,灯不会被点亮。所以使用“数组头位法”,应该尽量避免重复的字符。例如方法3
*/

/*
			中断函数中让控制LED1状态的方法3:
*/	
void UART_Handler() interrupt 4
{
	static int i = 0;
  char mark;
		if(RI == 1)
		{
			RI = 0;
			mark = SBUF;
			if(mark == 'o'|| mark == 'c')//如果发送来的是字符串中有'o'或者有'c'
			{
					i = 0;
			}
			dma_js[i] = mark;//则把o/c存放在数组dma_js的第一位
			i++;             //在o/c之后的字符依次存放。
			if(i == 5)       //字符串超过了数组,超过的部分依次从第一位开始存放
			{
				i = 0;
			}
			if(dma_js[0]=='o' && dma_js[1] == 'p')//op开灯
			{
					LED1 = 0;	
				  i = 0;
				memset(dma_js,'\0',2);
			}
			if(dma_js[0]=='c' && dma_js[1] == 'l')//cl开灯
			{
					LED1 = 1;
				   i = 0;
				memset(dma_js,'\0',2);			
			}
		}			
}
若上位机发送发字符串是open 第一次中断发生,i 等于 0,o被存储在dma_js[0]中,i 等于1。
第二个字符p被成功写入SBUF后,调用第二次中断函数,p被存储在dma_js[1]中,i等于2。
当最后一个字符n被成功写入SBUF中后,调用中断函数,n被存储在dma_js[3]中,i等于4。
然后就能进入下面的判断语句,让LED1点亮,然后让i 等于0(以便下次调用中断函数,让第一个写入SBUF里面的字符存储在dma_js数组的第一位)。
memset()是清空函数,把字符数组里面的字符串清空,以便存储下次来的字符串。

2、WiFi模块的使用

  WiFi模块的使用比蓝牙模块要复杂一些,因为WiFi模块需要AT指令对它进行一些配置。
下面是一些AT指令对WiFi模块的操作:

AT+RST//重启模块
AT+UART=9600,8,1,0,0                               //配置为波特率9600,8位数据传输,1位停止位
AT+CWMODE=3                                       //设置模块的工作模式,1是station(设备)模式 ,2是AP(路由)模式 ,3是双模
AT+CWJAP="ChinaNet-2.4G-8704","这里写你家的wifi密码"//模式接入家中路由器配置
AT+CIFSR                                          //查询模块的IP地址
AT+CIPSTART="TCP","192.168.101.174",8880          //让模块连接到移动设备的ip地址
AT+CIPSEND=4                                      //设置即将发送数据的长度 (这里是4个字节)
AT+CIPMODE=1                                      //开启透传模式
AT+CIPSEND                                        //wifi模块开始发送数据,如果WiFi模块只接收移动设备发送来的数据,这个指令不用配置
													如果是单片机通过wifi模块给移动设备发送数据,这个指令需要配置
                                                  //在透传发送数据过程中,若识别到单独的⼀包数据 “+++”,则退出透传发送
  • 电脑通过USB转TTL,然后连接到WiFi模块。所以电脑可以通过串口助手软件,向WiFi模块发出AT指令来对他进行配置

在这里插入图片描述
在这里插入图片描述

  • 其中蓝框里面的字符是,WiFi模块接收到AT指令后发出的回应,这些字符串在WiFi模块里面自动生成,然后返回发送给电脑。
    我们通过对这些字符串的辨别,来判断AT指令是否让WiFi模块配置成功。)

在这里插入图片描述

由此可见:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
如图,通过移动设备,将字符串发送给wifi模块,模块传送给PC1然后通过串口助手显示出来,
而PC1将wsnibb字符串通过串口助手发送给WiFi模块,模块将字符串发送给移动设备。

  • 将PC1换位单片机,且AT指令通过SBUF发送给WiFi模块,进行对模块的配置。并且单片机给WiFi模块发送字符串,然后wifi模块将字符串发送给PC2

代码:

#include <REGX52.H>
#include "intrins.h"
#include <string.h>

sfr AUXR = 0X8E;
sbit LED1 = P3^7;
char dma_js[6];
char dma_fs[] = "wohaoshuai\r\n";

code char CQWF[] = "AT+RST\r\n";//重启WiFi模块的指令
code char PZMS[] = "AT+CWMODE=3\r\n";
code char LJWL[] = "AT+CWJAP=\"ChinaNet-2.4G-8704\",\"18784970569\"\r\n";//连接家里路由器的指令
char LJIP[] = "AT+CIPSTART=\"TCP\",\"192.168.101.174\",8880\r\n";//连接PC2ip的指令
char TCMS[] = "AT+CIPMODE=1\r\n";//配置位透出模式的指令
char FSSJ[] = "AT+CIPSEND\r\n";//开始发送数据的指令

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	TR1 = 1;		//启动定时器1
}


void sendByte(char data_sj)
{
	SBUF = data_sj;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void main()
{
	int j ,k;
	j = k = 0;
	LED1 = 1;
	UartInit();
	
	sendString(CQWF);//重启wifi模块
	Delay1000ms();
	Delay1000ms();
	sendString(PZMS);
	Delay1000ms();
	Delay1000ms();
	sendString(LJWL);//连接家里路由器
	for(j = 0 ;j<=5 ;j++)   //需要延时,AT指令发送给WiFi模块后,wifi模块需要反应一段时间才会做出相应的动作
	{
			Delay1000ms();
	}
	sendString(LJIP);//连接移动设备ip
	for(k = 0; k<=5 ;k++)
	{
			Delay1000ms();
	}
	sendString(TCMS);//配置位透传模式
	Delay1000ms();
	sendString(FSSJ);//开始发送数据
	Delay1000ms();
	
	LED1 = 0; //执行完指令后让LED1点亮
	
	while(1)
	{
		Delay300ms();
		sendString(dma_fs);
		Delay1000ms();
	}
}

在这里插入图片描述
已然成功。

【注意】
1、要使用wifi模块前,先要知道连接WiFi模块移动设备的ip地址,以便进行AT指令的配置
2、要移动设备和WiFi模块要在同意路由器下。
3、配置WiFi模块的波特率和工作模式的指令,只需要配置一次,重新上电后就是配置后的样子。

  • 编写代码让移动设备通过WiFi模块控制单片机的LED1灯的亮灭。

代码:

#include <REGX52.H>
#include "intrins.h"
#include <string.h>

sfr AUXR = 0X8E;
sbit LED1 = P3^7;
char dma_js[2];
char dma_fs[] = "wohaoshuai\r\n";

code char CQWF[] = "AT+RST\r\n";
code char PZMS[] = "AT+CWMODE=3\r\n";
code char LJWL[] = "AT+CWJAP=\"ChinaNet-2.4G-8704\",\"18784970569\"\r\n";
char LJIP[] = "AT+CIPSTART=\"TCP\",\"192.168.101.174\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char FSSJ[] = "AT+CIPSEND\r\n";

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x40;		//8位数据,可变波特率
	REN = 1;
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	TR1 = 1;		//启动定时器1
}

void UART_interrupt_Init()
{
	ES = 1;//开启串口中断
	EA = 1;//开启中断总开关
}


void sendByte(char data_sj)
{
	SBUF = data_sj;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void main()
{
	int j ,k;
	j = k = 0;
	
		LED1 = 1;
		UartInit();
	
		sendString(CQWF);//重启wifi模块
		Delay1000ms();
		Delay1000ms();
	  sendString(PZMS);
		Delay1000ms();
		Delay1000ms();
		sendString(LJWL);//连接家里路由器
		for(j = 0 ;j<=5 ;j++)
		{
				Delay1000ms();
		}
		sendString(LJIP);//连接移动设备ip
		for(k = 0; k<=5 ;k++)
		{
				Delay1000ms();
		}
		sendString(TCMS);//配置位透传模式
		Delay1000ms();
		sendString(FSSJ);//开始发送数据
		Delay1000ms();
		
		UART_interrupt_Init();//打开中断
		
		LED1 = 0;//让LED1亮,代表WiFi模块连接成功

	while(1)
	{
		Delay300ms();
		sendString(dma_fs);
		Delay1000ms();
	}
}

void UART_Handler() interrupt 4
{
	static int i = 0;
	char data_js;
		if(RI)
		{
			RI = 0;
			data_js = SBUF;
			if(data_js == 'o' || data_js == 'c')
			{
				i = 0;
			}
			dma_js[i] = data_js;
			  i++;
		    if(i == 2)
			  i = 0;
			if(dma_js[0] == 'o' && dma_js[1] == 'p')//输出op
			{
				LED1 = 0;
				memset(dma_js,'\0',2);
			}	
			if(dma_js[0] == 'c' && dma_js[1] == 'l')//输入cl
			{
				LED1 = 1;
				memset(dma_js,'\0',2);
			}	
		}
}

其实这段代码不好,因为单片机给WiFi模块发送AT指令后,通过延时的方式来等待WiFi模块的操作,假如WiFi模块接收到AT指令后,做出相应的操作但是没有成功(例如连接移动设备的IP没有成功),我们也不知道。但是单片机依然会把下一个AT指令发送给WiFi模块,所以这是一个不好的现象。

我们可以通过wifi模块接收到AT指令后,对做出相应的回应的字符串进行判断,来设置下一步相应的操作,而不是无脑的发送AT指令给WiFi模块
例如:

在这里插入图片描述

蓝色框里面的字符串就是WiFi模块接收到AT指令并操作成功后,返回给单片机的字符串。单片机接收到这些字符串可以做出相应的判断。

代码优化:

#include <REGX52.H>
#include "intrins.h"
#include <string.h>

/*
		发送:AT+RST //重启WiFi模块
		返回:ready
		发送:AT+CWMODE=1 //配置工作模式
		返回:OK
		发送:AT+CWJAP="ChinaNet-2.4G-8704","18784970569"//连接路由器
		返回:WIFI CONNECTED
			  WIFI GOT IP
			  OK
		发送:AT+CIPSTART="TCP","192.168.101.2",8880//连接移动设备的IP
		返回:CONNECT
			  OK
		发送:AT+CIPMODE=1//透出模式
		返回:OK
		发送:AT+CIPSEND//开始发送
		返回:OK
*/


sfr AUXR = 0X8E;
sbit LED1 = P3^7;
sbit LED2 = P3^6;

char buffer[2];//定义2个字节的字符串数组,用来接收WiFi模块响应后,返回的字符串。
char dma_fs[] = "wohaoshuai\r\n";

code char CQWF[] = "AT+RST\r\n";									//wifi模块重启
code char LJWL[] = "AT+CWJAP=\"ChinaNet-2.4G-8704\",\"18784970569\"\r\n";
char LJIP[] = "AT+CIPSTART=\"TCP\",\"192.168.101.2\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";									//透传模式
char FSSJ[] = "AT+CIPSEND\r\n";										//开始发送

char Flag_OK = 0;
char Flag_IP = 0;

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
	
	ES = 1;//开启串口中断
	EA = 1;//开启中断总开关
}

void sendByte(char data_sj)
{
	SBUF = data_sj;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void main()
{
   LED1 = LED2 = 1;
   UartInit();
   Delay1000ms();

   sendString(CQWF);
   Delay300ms();
//1.连接路由器
   sendString(LJWL);
    while(!Flag_IP);	//WIFI GOT IP
    while(!Flag_OK);	//OK
	Flag_OK = 0;
	LED1 = 0;  //连接路由器成功后,LED1亮
//2.连接移动设备ip
	Delay300ms(); //延时一下,不让它做完判断后立马发送
	sendString(LJIP);
	while(!Flag_OK);
	Flag_OK = 0;
    LED2 = 0; //连接路IP成功后,LED2亮
//3.配置位透传模式	
    Delay300ms();	
    sendString(TCMS);
	while(!Flag_OK);
	 Flag_OK = 0;
//4.开始发送数据	 
	Delay300ms();
	sendString(FSSJ);
	while(!Flag_OK);
	LED2 = 1;  //可以开始发送数据后,LED2灭
       
	while(1)
	{
		Delay300ms();
		sendString(dma_fs);
		Delay1000ms();
	}
}

void UART_Handler() interrupt 4
{
	char temp;
	static int i = 0;
		if(RI)
		{
			RI = 0;
			temp = SBUF;
			if(temp == 'I'||temp == 'O'|| temp == 'o'|| temp == 'c')
			{
							i = 0;
			}
			buffer[i] = temp;
					i++;
			if(i == 2)
				i = 0;
			if(buffer[0] == 'I' && buffer[1] == 'P')//接收到的字符串为WIFI GOT IP中的IP
			{
						Flag_IP = 1;
					memset(buffer,'\0',2);		
			}
			
			if(buffer[0] == 'O' && buffer[1] == 'K')//接收到的字符串为OK
			{
						Flag_OK = 1;
					memset(buffer,'\0',2);		
			}
			
			if(buffer[0] == 'o' && buffer[1] == 'p')//输出open
			{
				LED1 = 0;
				memset(buffer,'\0',2);
			}	
			if(buffer[0] == 'c' && buffer[1] == 'l')//输入close
			{
				LED1 = 1;
				memset(buffer,'\0',2);
			}	
		}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值