蓝牙模块
又叫蓝牙串口模块。
串口透传技术:透传即透明传送,是指在数据的传输过程中,通过无线的方式使这组数据不发生任何形式的改变,仿佛传输过程是透明的一样,同时保证传输的质量,原封不动地道了最终接收者手里。
下载hc-08板子上二维码对应的HC蓝牙助手app,将hc08接入单片机(注意tx,rx反接),此时烧录以下代码:
#include "reg52.h"
#include <intrins.h>
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit led = P3^7;
char cmd[12];
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 UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //定义串口工作方式为方式1,8位UART
//PCON初值为00x1 0000符合我们的要求,我们只需要SMOD=0不加倍即可
AUXR = 0x01;
TMOD &= 0x0F;
TMOD |= 0x20; //配置定时器1为8为自动重载定时器
TL1 = 0xFD;
TH1 = 0xFD; //9600波特率初值
TR1 = 1; //打开中断
EA = 1; //开启总中断
ES = 1; //开启UART中断
}
void sendByte(char data_msg){
SBUF = data_msg;
//当8位数据没有传送结束时,卡在while循环中
while(!TI); //TI发送中断请求标志位,第8位数据传送结束时,硬件自动置1
TI = 0;
}
void sendString(char* str){
while(*str != '\0'){
sendByte(*str++);
}
}
void main()
{
led = 1;
//配置C51串口的通信方式
UartInit();
while(1){
Delay1000ms();
//往发送缓冲区写入数据,就完成数据的发送
sendString("hello world\r\n");
}
}
void Uart_Handler() interrupt 4
{
static int i = 0; //
if(RI){ //中断处理函数中,对于接收中断的响应
RI = 0;
cmd[i] = SBUF;
i++;
if(i == SIZE) i=0;
if(strstr(cmd,"en")){ //判断cmd中是否有“en”子串
led = 0;
i=0;
memset(cmd,'\0',SIZE);
}
if(strstr(cmd,"se")){
led = 1;
i=0;
memset(cmd,'\0',SIZE);
}
}
if(TI);
}
打开手机app即可每隔一秒接收到一个hello world同时可以通过手机发送open或close开/关灯.
同时可以通过AT指令进行快乐的玩耍
WIFI模块
初始配置和验证
为了将来与单片机的波特率保持一致,将波特率修改为9600
AT+UART=9600,8,1,0,0
入网设置
1.设置工作模式
AT+CWMODE=3 //1.是station(设备)模式2.是AP(路由)模式3.双模
OK
2.以设备模式接入家中的路由器
AT+CWJAP="wifipig","66668888"
WIFI CONNECTED
WIFI GOT IP
OK
3.查询IP地址
AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"a6:cf:12:c1:ec:eb"
+CIFSR:STAIP,"192.168.31.46"
+CIFSR:STAMAC,"a4:cf:12:c1:ec:eb"
OK
4.连接服务器
AT+CIPSTART="TCP","192.168.31.152",8880
CONNECT
OK
5.发送数据
AT+CIPSEND=4 //设置即将发送的数据长度 4个字节
> //注意在发送数据的时候要取消勾选发送新行,在上传指令的时候要勾选发送新行
Recv 4 bytes
SEND OK
透传发送数据
基于上述连接服务器之后
AT+CIPMODE=1 //开启透传模式
OK
AT+CIPSEND
> //即可肆意传输 当你不想传输了发送一个“+++”即可退出透传模式
将wifi模块接入单片机进行开发
首先我们将以下代码烧录至单片机中:
#include "reg52.h"
#include <intrins.h>
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit led = P3^7;
char cmd[SIZE];
code char LJWL[] = "AT+CWJAP=\"wifipi\",\"66668888\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.31.152\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[] = "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 UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //定义串口工作方式为方式1,8位UART
//PCON初值为00x1 0000符合我们的要求,我们只需要SMOD=0不加倍即可
AUXR = 0x01;
TMOD &= 0x0F;
TMOD |= 0x20; //配置定时器1为8为自动重载定时器
TL1 = 0xFD;
TH1 = 0xFD; //9600波特率初值
TR1 = 1; //打开中断
EA = 1; //开启总中断
ES = 1; //开启UART中断
}
void sendByte(char data_msg){
SBUF = data_msg;
//当8位数据没有传送结束时,卡在while循环中
while(!TI); //TI发送中断请求标志位,第8位数据传送结束时,硬件自动置1
TI = 0;
}
void sendString(char* str){
while(*str != '\0'){
sendByte(*str++);
}
}
void main()
{
led = 1;
//配置C51串口的通信方式
UartInit();
while(1){
//Delay1000ms();
//往发送缓冲区写入数据,就完成数据的发送
//sendString("hello world\r\n");
sendString(LJWL);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(LJFWQ);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(TCMS);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(SJCS);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
}
}
void Uart_Handler() interrupt 4
{
static int i = 0; //
if(RI){ //中断处理函数中,对于接收中断的响应
RI = 0;
cmd[i] = SBUF;
i++;
if(i == SIZE) i=0;
if(strstr(cmd,"en")){ //判断cmd中是否有“en”子串
led = 0;
i=0;
memset(cmd,'\0',SIZE);
}
if(strstr(cmd,"se")){
led = 1;
i=0;
memset(cmd,'\0',SIZE);
}
}
if(TI);
}
依次传输连接网络、连接服务器、透传模式、数据传输的指令,先传到电脑上检查一个格式是否正确。
之后可以将wifi模块接入单片机,依次将上述指令传输给wifi模块,但是我们如何知道wifi模块是否正确接收到指令并返回正确的回复?
我们可以通过单片机传输指令至8266wifi模块,之后将8266的tx与pc端串口助手的rx连接,查看8266返回的数据以检查是否正确接收指令并作出正确的回复。
可以看到8266模块确实正确接收到了指令,并且成功连接网络、连接服务器、开启透传模式进行了数据传输,因此代码正确。
此时我们通过网络向8266发送数据后,由于8266的tx口连着pc端因此会在pc端的串口助手中接收到数据,当我们将8266的tx口与单片机相连,就可以实现通过网络来操控单片机的效果。
通过TCP通信控制LED灯
将8266与单片机相连,修改串口中断中的代码:
#include "reg52.h"
#include <intrins.h>
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit led = P3^7;
char cmd[SIZE];
code char LJWL[] = "AT+CWJAP=\"wifipi\",\"66668888\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.31.152\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[] = "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 UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //定义串口工作方式为方式1,8位UART
//PCON初值为00x1 0000符合我们的要求,我们只需要SMOD=0不加倍即可
AUXR = 0x01;
TMOD &= 0x0F;
TMOD |= 0x20; //配置定时器1为8为自动重载定时器
TL1 = 0xFD;
TH1 = 0xFD; //9600波特率初值
TR1 = 1; //打开中断
EA = 1; //开启总中断
ES = 1; //开启UART中断
}
void sendByte(char data_msg){
SBUF = data_msg;
//当8位数据没有传送结束时,卡在while循环中
while(!TI); //TI发送中断请求标志位,第8位数据传送结束时,硬件自动置1
TI = 0;
}
void sendString(char* str){
while(*str != '\0'){
sendByte(*str++);
}
}
void main()
{
int mark = 0;
led = 1;
//配置C51串口的通信方式
UartInit();
while(1){
//Delay1000ms();
//往发送缓冲区写入数据,就完成数据的发送
//sendString("hello world\r\n");
if(mark == 0){
sendString(LJWL);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(LJFWQ);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(TCMS);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
sendString(SJCS);
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
Delay1000ms();
mark = 1;
}else{
sendString("hello world\r\n");
Delay1000ms();
}
}
}
void Uart_Handler() interrupt 4
{
if(RI){ //中断处理函数中,对于接收中断的响应
RI = 0;
cmd[0] = SBUF;
if(cmd[0] == '1'){
led = 0;
}
if(cmd[0] == '0'){
led = 1;
}
}
if(TI);
}
但是这段代码在运行时,我们无法看到任何连接的信息,不知道连接是否正确进行,因此代码是不完善的,在有时候我们无法连接上wifi因此我们还是需要借助上述白盒测试的方法来检查返回值,程序可能会因为在5s内连接不上wifi而导致后续一系列操作都发生错误,因此我们需要优化我们的代码:
基本上当我能够正确接入wifi后续的操作就会成功,而接入wifi后会返回的提示为WIFI CONNECTED
WIFI GOT IP
根据程序正确运行时的返回值来进行判断.
这里记录一下在编程过程中遇到的一个错误卡了我好久
void Uart_Handler() interrupt 4
{
static int i = 0;
char tmp;
if(RI){ //中断处理函数中,对于接收中断的响应
RI = 0;
tmp = SBUF;
if(tmp == 'W' || tmp == 'O' || tmp == 'L'){
i = 0;
}
buffer[i] = tmp;
i++;
if(buffer[0] == 'W' && buffer[5] == 'G'){ //当显示WIFI GOR IP时表示网络已经接入,修改标志位
AT_Connect_Net_Flag = 1;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'O' && buffer[1] == 'K'){
AT_OK_Flag = 1;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'L' && buffer[2] == '1'){
D5 = 0;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'L' && buffer[2] == '0'){
D6 = 1;
memset(buffer,'\0',SIZE);
}
if(i == 12) i=0;
memset(buffer,'\0',SIZE);//出错了!!!!
}
if(TI);
}
在调试过程中,我用之前的白盒测试方法来进行检验,但是当我给单片机发送OK信号时,没有用,卡了很久,最后发现是,倒数第四行多写了一个memset。这段代码的含义是,每次写入一个字节后,会对每个写入的字符进行判断,如果是W、O、L开头,会强制将下标变为0,为了能够方便下面的判断,因为当我们连入wifi后,8266模块会返回WIFI GOT IP,第一个字符为W,第6个字符为G,因此我们可以判断buffer[0]是否为W,buffer[5]是否为G从而判断是否成功入网,如果满足条件,将入网标志位置一,同时清空buffer数组。但是上述代码会在我每次读取完一个字符后,将buffer清空,因此永远也无法达到效果。
修改完毕后,就可以达到我们需要的效果了:
检验完毕后,我们直接连接到单片机即可。
#include "reg52.h"
#include <intrins.h>
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
sbit D6 = P3^6;
char buffer[SIZE];
code char LJWL[] = "AT+CWJAP=\"wifipig\",\"66668888\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.31.152\",8880\r\n";
char RESET[] = "AT+RST\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[] = "AT+CIPSEND\r\n";
char AT_Connect_Net_Flag = 0;
char AT_OK_Flag = 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 UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //定义串口工作方式为方式1,8位UART
//PCON初值为00x1 0000符合我们的要求,我们只需要SMOD=0不加倍即可
AUXR = 0x01;
TMOD &= 0x0F;
TMOD |= 0x20; //配置定时器1为8为自动重载定时器
TL1 = 0xFD;
TH1 = 0xFD; //9600波特率初值
TR1 = 1; //打开中断
EA = 1; //开启总中断
ES = 1; //开启UART中断
}
void sendByte(char data_msg){
SBUF = data_msg;
//当8位数据没有传送结束时,卡在while循环中
while(!TI); //TI发送中断请求标志位,第8位数据传送结束时,硬件自动置1
TI = 0;
}
void sendString(char* str){
while(*str != '\0'){
sendByte(*str++);
}
}
void main()
{
int mark = 0;
D5 = D6 = 1;
//配置C51串口的通信方式
UartInit();
Delay1000ms(); //给wifi模块上电时间
sendString(LJWL);
while(!AT_Connect_Net_Flag);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
sendString(LJFWQ);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
sendString(TCMS);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
sendString(SJCS);
while(!AT_OK_Flag);
if(AT_Connect_Net_Flag){
D5 = 0; //点亮第一盏灯说明入网成功
}
if(AT_OK_Flag){
D6 = 0; //点亮第二盏led说明网络连接成功并成功打开透传模式
}
while(1){
Delay1000ms();
sendString("hello world\r\n");
}
}
void Uart_Handler() interrupt 4
{
static int i = 0;
char tmp;
if(RI){ //中断处理函数中,对于接收中断的响应
RI = 0;
tmp = SBUF;
if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == "F"){
i = 0;
}
buffer[i] = tmp;
i++;
if(buffer[0] == 'W' && buffer[5] == 'G'){ //当显示WIFI GOR IP时表示网络已经接入,修改标志位
AT_Connect_Net_Flag = 1;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'O' && buffer[1] == 'K'){
AT_OK_Flag = 1;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'F' && buffer[1] == 'A'){
for(i=0;i<5;i++){
D5 = 0;
Delay1000ms();
D5 = 1;
Delay1000ms();
}
//sendString(RESET);
memset(buffer, '\0', SIZE);
}
if(buffer[0] == 'L' && buffer[2] == '1'){
D5 = 0;
memset(buffer,'\0',SIZE);
}
if(buffer[0] == 'L' && buffer[2] == '0'){
D5 = 1;
memset(buffer,'\0',SIZE);
}
if(i == 12) i=0;
}
//if(TI);
}
当成功连入后,两盏灯都会点亮,可以用过L-1点亮D5,L-0熄灭D5。