STM32(F103ZET6)第十六课:WIFI模块的配置与应用

目录

需求

完成WiFi模块的配置,使其能连接服务器并最终能和服务器相互发送消息。

一、wifi模块简述

本项目开发版上没有封装好的WIFI模块,所以借助外部wifi模块。

ESP8266:乐鑫公司推出的一款无线SOC。可以直接使用ESP_IDF、Arduino直接编程

ESP_12F:安信可公司在ESP8266芯片的基础上,封装出的一个模块

模块原理图:

模块引脚配置:

通信接口配置:

二、配置流程

1.配置通信串口

配置串口3(本次使用的WiFi模块串口接的是串口3)

默认配置: 波特率115200  8位数据位 0位校验位 1位停止位

配置PB10(TX) PB11(RX)

TX:复用推挽 RX:浮空输入
配置PE6(ESP模块的使能引脚)

高电平使能
代码编写:

bash 复制代码
/*****************************************************************
 *函 数 名 称:USART3_Config
 *函 数 功 能:初始化串口3
 *函 数 形 参:无
 *函 数 返 回:无
*******************************************************************/
void USART3_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct={0};
	USART_InitTypeDef USART_InitStruct={0};
	NVIC_InitTypeDef NVIC_InitStruct={0};
	//1,开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	//2,配置IO口
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	//3,配置串口
	USART_InitStruct.USART_BaudRate=115200;
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStruct.USART_Parity=USART_Parity_No;  
	USART_InitStruct.USART_StopBits=USART_StopBits_1;
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;

	USART_Init(USART3,&USART_InitStruct);
	//4,使能串口
	USART_Cmd(USART3,ENABLE);
	//5,开中断
	USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
	USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
	//6,配置中断
	NVIC_InitStruct.NVIC_IRQChannel=USART3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级
	NVIC_Init(&NVIC_InitStruct);
}
/*****************************************************************
 *函 数 名 称:WIFI_Config
 *函 数 功 能:初始化WIFI
 *函 数 形 参:无
 *函 数 返 回:无
*******************************************************************/
void WIFI_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct={0};
	//配置通信串口
	USART3_Config();
	//配置使能引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	//2,配置IO口
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	
	GPIO_SetBits(GPIOE,GPIO_Pin_6);
	Delay_nms(500);//延时让WIFI输出固件信息
}

2.配置引脚与中断接受

在中断中进行信息的接受

bash 复制代码
/*****************************************************************
 *函 数 名 称:USART3_IRQHandler
 *函 数 功 能:初始化串口3
 *函 数 形 参:无
 *函 数 返 回:无
*******************************************************************/
void USART3_IRQHandler(void)
{
	uint8_t data=0;
	if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET)
	{
		data = USART3->DR;
		rxdata.rxbuff[rxdata.rxcnt++] = data;
		rxdata.rxcnt %= MAX;//确保不越界
		USART1->DR = data;//通过串口1将数据发给电脑串口助手
	}
	if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET)
	{
		rxdata.rxflag = 1;
		data = USART3->SR;
		data = USART3->DR;
	}	
}

3.简述AT指令

AT 命令(AT Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制 MODEM 而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号 MODEM 基本退出一般使用市场,但是 AT 命令保留下来。

在嵌入式开发中,经常是使用AT命令去控制各种通讯模块,比如ESP8266 WIFI模块、4G模块、GPRS模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送AT命令给通讯模块,模块接收到数据之后回应响应的数据。

AT指令的分类:


AT指令的使用:

要注意:基本所有AT指令,结尾必须换行(\r\n),除"+++"以外

ESP12F模块的工作模式:STA(连接热点) AP(释放热点) STA+AP

STA模式:模组作为节点去连接热点,然后就可连接某个服务器。

AP模式:模组作为热点,释放网络,可以在模组上创建服务器,其他设备连接他。

该模块的指令:
AT :测试固件的
AT+RST :重启ESP8266
ATE0 :关闭回显
ATE1 :打开回显
AT+CWMODE_DEF(_DEF有些固件支持,有些不支持)=x
x=1为设置工作模式 STA模式(可以连接其他设备热点),2为AP模式,3为组合模式。
AT+CWJAP_DEF="WIFI名","WIFI密码" 连接WIFI。
AT+CWSAP :配置 ESP8266 SoftAP 参数(配置释放的热点) 。
AT+CIPAP :设置 ESP8266 SoftAP 的 IP 地址。
多连接情况下 (AT+CIPMUX=1),才能开启 TCP 服务器。
AT+CIPSERVER :建⽴ TCP 服务器
AT+CIPSTART="TCP","IP",端口号 以TCP的形式连接服务器
AT+CIPMODE=1 : 开启透传 (向wifi发送的所有消息(除+++外)都认为不是指令)
AT+CIPSEND : 启动发送功能

+++ 没有回车 : 退出透传
AT+CIPCLOSE : 退出服务器连接

以上的指令都是比较常用的,所以应当熟记。

连接服务器需要那些步骤:

1.连接网络

2.设置位连接热点模式:STA

3.连接热点:名字和密码

4.连接服务器 ip 和 端口

5.收发数据

AT是AT指令还是收发的数据

透传模式(透明传输,所有消息都认为是普通收发的消息)

4.程序编写

给串口发送AT指令,相当于发送字符串。

本项目相当于WIFI模块经过串口三发给串口一,经过串口一发送给串口助手。

发送单个字符的代码:

bash 复制代码
/*****************************************************************
 *函 数 名 称:USART3_SendData
 *函 数 功 能:串口3发送单个字节
 *函 数 形 参:data :发送的内容
 *函 数 返 回:无
*******************************************************************/
void USART3_SendData(uint8_t data)
{
	while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET);
	USART3->DR = data;
	while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);
}

发送字符串:

bash 复制代码
/*****************************************************************
 *函 数 名 称:USART3_SendStr
 *函 数 功 能:串口3发送字符串
 *函 数 形 参:Str:发送的内容	 len:长度
 *函 数 返 回:无
*******************************************************************/
void USART3_SendStrLen(uint8_t *Str,uint16_t len)
{
	uint16_t i=0;
	for(i=0;i<len;i++)
	{
		USART3_SendData(Str[i]);
	}
}

定义一个WIFI的结构体进行信息的收发。

再写一个指令发送的函数.

bash 复制代码
/*****************************************************************
 *函 数 名 称:WIFI_SendCmd
 *函 数 功 能:发送指令,并且判断是否发送成功
 *函 数 形 参:CMD:发送的指令内容
							 ACK:期待回复的数据
							 TIME:等待时间 单位ms
 ***函 数 返 回:成功返回0 失败返回1**
*******************************************************************/
uint8_t WIFI_SendCmd(uint8_t* CMD,uint8_t* ACK,uint16_t TIME)
{
	memset(&rxdata,0,sizeof(rxdata));//清数据
	USART3_SendStr(CMD);
	while(TIME--)//超时检测机制
	{
		if(rxdata.rxflag == 1) //接受完成之后再查找
		{
			rxdata.rxflag = 0;
			if(strstr((char *)rxdata.rxbuff,(char *)ACK) != NULL)
			{
				memset(&rxdata,0,sizeof(rxdata));//清数据
				return 0;//查到正确返回值
			}
		}
		Delay_nms(1);
	}
	memset(&rxdata,0,sizeof(rxdata));//清数据
	return 1;//规定时间没查到
}

其中strstr函数为查询目标字符串种是否有所需字符串,若有则返回所需字符串的地址,没有则返回0。

strstr(目标字符串,所需字符串)

链接服务器的函数:

bash 复制代码
/*****************************************************************
 *函 数 名 称:WIFI_ConnectSever
 *函 数 功 能:WIFI链接服务器
 *函 数 形 参:无
 *函 数 返 回:成功返回0 失败返回1
*******************************************************************/
uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port)
{
	char buff[100]={0};
	if(WIFI_SendCmd((u8*)"AT\r\n",(u8*)"OK",100) == 0)
	{
		printf("WIFI 正常工作\r\n");
		if(WIFI_SendCmd((u8*)"AT+CWMODE=1\r\n",(u8*)"OK",100) == 0)
		{
			printf("WIFI 模式设置完成\r\n");
			if(WIFI_SendCmd((u8*)"AT+CWJAP=\"cmyddf\",\"88888888\"\r\n",(u8*)"OK",30000) == 0)
			{
				printf("WIFI 热点链接完成\r\n");
				sprintf(buff,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",severIP,Port);
				if(WIFI_SendCmd((u8*)buff,(u8*)"OK",3000) == 0)
				{
					printf("WIFI 服务器链接完成\r\n");
					WIFI_SendCmd((u8*)"AT+CIPMODE=1\r\n",(u8*)"OK",3000);
					WIFI_SendCmd((u8*)"AT+CIPSEND\r\n",(u8*)"OK",3000);
					printf("请发送数据\r\n");
					return 0;
				}
				return 1;
			}
			return 1;
		}
		return 1;
	}
	return 1;
}

三、需求实现代码

main.c

bash 复制代码
//WIFI任务
TaskHandle_t Ali_TaskHandle;
void Ali_Task(void *p)
{
		uint16_t cnt=0;
	  MyPrintf("Ali_Task begin\r\n"); //传感器开始
		WIFI_ConnectSever(Sever_IP, SeverPort);//链接wifi
		MQTT_Connect(ClientID, UserName,PassWord,3000);//发布报文
		Delay_nms(1000);
		MQTT_Subscribe(Subscribe_Topic);//订阅时钟同步主题
		Delay_nms(1000);	
		MQTT_Publish(Publish_NTPTopic,(u8*)"{\"deviceSendTime\":\"123\"}");
	while(1)
	{	cnt++;
		WIFI_Anylze();
		vTaskDelay(10);
		if(cnt>=500)
		{
			cnt=0;
			MQTT_Publish(Publish_Topic,(u8 *)Payload);
		}		
	}
}

wifi.c

bash 复制代码
#include "wifi.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
#include "led.h"
#include "stdlib.h"
#include "rtc.h"

WIFI_DATA rxdata={0};

//串口3 PB10 - TX  PB11 - RX
/*****************************************************************
 *函 数 名 称:USART3_Config
 *函 数 功 能:初始化串口3
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct={0};
	USART_InitTypeDef USART_InitStruct={0};
	NVIC_InitTypeDef NVIC_InitStruct={0};
	//1,开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	//2,配置IO口
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	//3,配置串口
	USART_InitStruct.USART_BaudRate=115200;
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStruct.USART_Parity=USART_Parity_No;  
	USART_InitStruct.USART_StopBits=USART_StopBits_1;
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;

	USART_Init(USART3,&USART_InitStruct);
	//4,使能串口
	USART_Cmd(USART3,ENABLE);
	//5,开中断
	USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
	USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
	//6,配置中断
	NVIC_InitStruct.NVIC_IRQChannel=USART3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级
	NVIC_Init(&NVIC_InitStruct);
}

/*****************************************************************
 *函 数 名 称:WIFI_Config
 *函 数 功 能:初始化WIFI
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void WIFI_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct={0};
	//配置通信串口
	USART3_Config();
	//配置使能引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	//2,配置IO口
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	
	GPIO_SetBits(GPIOE,GPIO_Pin_6);
	Delay_nms(500);//延时让WIFI输出固件信息
}

/*****************************************************************
 *函 数 名 称:USART3_IRQHandler
 *函 数 功 能:初始化串口4
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx 01 02 03 
*******************************************************************/
void USART3_IRQHandler(void)
{
	uint8_t data=0;
	if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET)
	{
		data = USART3->DR;
		rxdata.rxbuff[rxdata.rxcnt++] = data;
		rxdata.rxcnt %= MAX;//确保不越界
		USART1->DR = data;//通过串口1将数据发给电脑串口助手
	}
	if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET)
	{
		rxdata.rxflag = 1;
		data = USART3->SR;
		data = USART3->DR;
	}	
}

/*****************************************************************
 *函 数 名 称:USART3_SendData
 *函 数 功 能:串口3发送单个字节
 *函 数 形 参:data :发送的内容
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_SendData(uint8_t data)
{
	while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET);
	USART3->DR = data;
	while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);
}

/*****************************************************************
 *函 数 名 称:USART3_SendStr
 *函 数 功 能:串口3发送字符串
 *函 数 形 参:Str:发送的内容
							 len:长度
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_SendStrLen(uint8_t *Str,uint16_t len)
{
	uint16_t i=0;
	for(i=0;i<len;i++)
	{
		USART3_SendData(Str[i]);
	}
}

void USART3_SendStr(uint8_t *Str)
{
	while(*Str != '\0')
	{
		USART3_SendData(*Str);
		Str++;
	}
}

/*****************************************************************
 *函 数 名 称:WIFI_SendCmd
 *函 数 功 能:发送指令,并且判断是否发送成功
 *函 数 形 参:CMD:发送的指令内容
							 ACK:期待回复的数据
							 TIME:等待时间 单位ms
 *函 数 返 回:成功返回0 失败返回1
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
uint8_t WIFI_SendCmd(uint8_t* CMD,uint8_t* ACK,uint16_t TIME)
{
	memset(&rxdata,0,sizeof(rxdata));//清数据
	USART3_SendStr(CMD);
	while(TIME--)//超时检测机制
	{
		if(rxdata.rxflag == 1) //接受完成之后再查找
		{
			rxdata.rxflag = 0;
			if(strstr((char *)rxdata.rxbuff,(char *)ACK) != NULL)
			{
				memset(&rxdata,0,sizeof(rxdata));//清数据
				return 0;//查到正确返回值
			}
		}
		Delay_nms(1);
	}
	memset(&rxdata,0,sizeof(rxdata));//清数据
	return 1;//规定时间没查到
}

/*****************************************************************
 *函 数 名 称:WIFI_ConnectSever
 *函 数 功 能:WIFI链接服务器
 *函 数 形 参:无
 *函 数 返 回:成功返回0 失败返回1
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port)
{
	char buff[100]={0};
	if(WIFI_SendCmd((u8*)"AT\r\n",(u8*)"OK",100) == 0)
	{
		printf("WIFI 正常工作\r\n");
		if(WIFI_SendCmd((u8*)"AT+CWMODE=1\r\n",(u8*)"OK",100) == 0)
		{
			printf("WIFI 模式设置完成\r\n");
			if(WIFI_SendCmd((u8*)"AT+CWJAP=\"cmyddf\",\"88888888\"\r\n",(u8*)"OK",30000) == 0)
			{
				printf("WIFI 热点链接完成\r\n");
				sprintf(buff,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",severIP,Port);
				if(WIFI_SendCmd((u8*)buff,(u8*)"OK",3000) == 0)
				{
					printf("WIFI 服务器链接完成\r\n");
					WIFI_SendCmd((u8*)"AT+CIPMODE=1\r\n",(u8*)"OK",3000);
					WIFI_SendCmd((u8*)"AT+CIPSEND\r\n",(u8*)"OK",3000);
					printf("请发送数据\r\n");
					return 0;
				}
				return 1;
			}
			return 1;
		}
		return 1;
	}
	return 1;
}


/*****************************************************************
 *函 数 名 称:WIFI_Anylze
 *函 数 功 能:WIFI接受解析
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void WIFI_Anylze(void)
{
	uint32_t GetTime=0;
	uint32_t nowtime=0;
	char *p=NULL;
	char buff[12]={0};
	uint8_t i = 0;
	if(rxdata.rxflag == 1)
	{
		if(rxdata.rxbuff[0] == '1')
			Led_ON(1);
		if(rxdata.rxbuff[0] == '0')
			Led_OFF(1);
		if(strstr((const char *)rxdata.rxbuff+10,"serverSendTime") != NULL )
		{
			printf("收到时间同步消息\r\n");
			p = strstr((const char *)rxdata.rxbuff+10,"serverSendTime");
			p += 17;
			for(i=0;i<10;i++)
			{
				buff[i] = p[i];
			}
			GetTime=atoi(buff);
			printf("提取出的时间结果:%d\r\n",GetTime);
			//将获取到的时间,写入到RTC的计数器里即可。
			//RTC_Configuration(GetTime);
			nowtime=GetTime+8*60*60;
			RTC_Configuration(nowtime);
		}
		if(strstr((const char *)rxdata.rxbuff+10,"\"powerstate\":1") != NULL )
		{
			Led_ON(1);
		}
		if(strstr((const char *)rxdata.rxbuff+10,"\"powerstate\":0") != NULL )
		{
			Led_OFF(1);
		}
		memset(&rxdata,0,sizeof(rxdata));
	}	
}

wifi.h

bash 复制代码
#ifndef __WIFI_H
#define __WIFI_H

#include "stm32f10x.h"

#define MAX 1024

typedef struct{
	uint8_t rxbuff[MAX];//保存接受数据
	uint16_t rxcnt;//记录接受数据个数
	uint8_t rxflag;//接受完成标志
}WIFI_DATA;
extern WIFI_DATA rxdata;

uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port);
void USART3_SendStrLen(uint8_t * Str,uint16_t len);
void WIFI_Config(void);
void WIFI_Anylze(void);

#endif
相关推荐
阿川!3 小时前
嵌入式软件--51单片机 DAY 4
单片机·嵌入式硬件·51单片机
好家伙VCC6 小时前
嵌入式 面试单片机面试题目分析和答案 IIC总线 IIC协议 SPI总线 I2C协议讲解 SPI和IIC协议对比比较SPI的工作模式比较 IIC基本概念讲解
单片机·嵌入式硬件
秋风&萧瑟6 小时前
【STM32】CAN总线基础入门
stm32·单片机·嵌入式硬件
LCMICRO-133108477466 小时前
长芯微LS5541/LS5542国产数模转换芯片DAC完全替代P2P替代AD5541/AD5542
单片机·嵌入式硬件·fpga开发·硬件工程·dsp开发
浅陌pa7 小时前
19:I2C一:程序模拟I2C通信时序
stm32·单片机·嵌入式硬件
7yewh7 小时前
STM32常用数据采集滤波算法
stm32·嵌入式硬件·算法
Chambor_mak8 小时前
stm32单片机个人学习笔记1(简单介绍)
stm32·单片机·学习
lantiandianzi9 小时前
基于单片机的热成像测温显示系统设计
单片机·嵌入式硬件