STM32第十六课:WiFi模块的配置及应用

文章目录

  • 需求
  • 一、WiFi模块概要
  • 二、配置流程
  • 1.配置通信串口,引脚和中断
  • 2.AT指令
  • 3.发送逻辑编写
  • 三、需求实现代码
  • 总结

  • 需求

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


    一、WiFi模块概要

    本次使用的WiFi模块为ESP-12F模块(安信可)
    驱动芯片为ESP8266(乐鑫)。
    ESP8266的使用:
    1.作为mcu开发,再次搭建一下它的环境,开发周期较长。
    2.利用官方固件使用AT指令(AT+**)开发。

    芯片引脚:

    通信接口:

    二、配置流程

    1.配置通信串口,引脚和中断

    配置串口3(本次使用的WiFi模块串口接的是串口3)
    默认配置: 波特率115200  8位数据位 0位校验位 1位停止位

    配置PB10(TX) PB11(RX)
    TX:复用推挽 RX:浮空输入

    配置PE6(ESP模块的使能引脚)
    高电平使能

    void Esp8266_Config()
    {
    	  //开时钟:GPIOB,USART3
    	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
    	
    	    //配置对应的IO口 PB10(tx):复用推挽 PB11(RX):浮空输入
    	    GPIO_InitTypeDef GPIO_InitStruct = {0};
    		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);
    		//PE6
    		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);
    		
    		
    	    //配置串口3  8数据位,0校验位,1停止位,波特率115200
    		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
    		USART_InitStruct.USART_BaudRate = 115200;//波特率
    		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
    		USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
    		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);
    		USART_Cmd(USART3,ENABLE);
            //配置串口3的中断
    		USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
    		NVIC_SetPriority(USART3_IRQn,7);//设置优先级0~15
    		NVIC_EnableIRQ(USART3_IRQn);//使能中断通道
    		GPIO_SetBits(GPIOE,GPIO_Pin_6);
    	    Delay_nms(500);
    }
    

    最后加的延时是为了保证所有配置都配置完。

    2.AT指令

    在配置中断的发送和接收前,我们要了解一下AT指令。
      AT 命令(AT Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制 MODEM 而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号 MODEM 基本退出一般使用市场,但是 AT 命令保留下来。
      在嵌入式开发中,经常是使用AT命令去控制各种通讯模块,比如ESP8266 WIFI模块、4G模块、GPRS模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送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指令还是收发的数据
    透传模式(透明传输,所有消息都认为是普通收发的消息)

    3.发送逻辑编写

    给串口发送命令,可以理解为发送字符串。
    为了实现发送字符串,我们先写一个能发送单字节的函数

    void Usart3Senddata(uint8_t data)
    {
    	//等待发送完成
    	while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==0);
    	//如果上次发送完成,就发送
    	USART_SendData(USART3,data);
    }
    

    由于字符串的末尾为"\0",我们结合单字节发送函数就能实现字符串的发送。

    void U3_SendStr(uint8_t * data)
    {
    	while(*data!='\0')
    	{
    		Usart3Senddata(*data);
    		data++;
    	}
    }
    

    一个一个字符发送,发送一个data+1,遇到反斜杠0结束。
    为了能够将发送的命令保存起来。
    我们先定义一个结构体方便后续操作。

    typedef struct{
    	uint8_t recvbuf[1024];
    	uint16_t recvcnt;//保存命令的条数,起到计数作用,防溢出。
    }WIFIDATA;
    


    此时我们写一个WiFi发送命令的函数 Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
    参数:命令,期待返回值,超时时间
    之所以设置超时时间,是因为这些指令的执行都需要时间,反应较慢。

    uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
    {
    	uint32_t timecnt=0;
    	memset(&wifidata,0,sizeof(wifidata));//先清空
    	U3_SendStr((uint8_t *)cmd);//发送命令
    	while(strstr((char *)wifidata.recvbuf,recv)==NULL)
    	{
    	timecnt++;
    	Delay_nms(1);
    		if(timecnt>=timeout){
    		printf("发送超时失败%s",cmd);
    		return 1;
    	 }
    	}
    	printf(" 发送成功 ");
    	return 0;
    }
    

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

    然后就是写IP链接函数了

    uint8_t Wifi_ConnectIP(void)
    {
    	if(Wifi_Send_Cmd("AT\r\n","OK",1000) != 0){//测试
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CWMODE=1\r\n","OK",2000) != 0){//设置为STA
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CWJAP=\"LEGION-5169\",\"88888888\"\r\n","OK",10000)!= 0){//连接热点
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPSTART=\"TCP\",\"36.137.226.30\",37233\r\n","OK",10000)!= 0){//连接服务器
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPMODE=1\r\n","OK",1000)!= 0){//开启透传
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPSEND\r\n","OK",1000)!= 0){//启动发送功能
    		return 1;
    	}
    	return 0;	
    }
    

    要注意:"需要反斜杠转义

    最后编写串口3和串口1的中断函数:

    void USART3_IRQHandler(void)
    {
    	
    	uint8_t data=0;
    	if((USART3->SR&0x1<<5)!=0)
    	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
    		data = USART_ReceiveData(USART3);//读操作,同时也是清空中断标志位
    		wifidata.recvbuf[wifidata.recvcnt] = data;
    		wifidata.recvcnt++;
    		wifidata.recvcnt%=1024;
    		USART_SendData(USART1, data); 
    	}
    }
    
    void USART1_IRQHandler(void)
    {
    	uint8_t data=0;
    	if((USART1->SR&0x1<<5)!=0)
    	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
    		//接收数据
    		data = USART_ReceiveData(USART1);//读操作,同时也是清空中断标志位
         	USART3->DR = data;//发送数据
    		//USART_SendData(USART5, data); 
    	}
    }
    
    

    逻辑如下:
    串口3先读接收到的数据,然后将数据保存到结构体中,结构体中的计数器++并对1024取余防止溢出,最后将数据发送给串口1。
    串口1先读接收到的数据,后将数据发送给串口3。

    三、需求实现代码

    main.c

    #include "stm32f10x.h"
    #include "usart.h"
    #include "stdio.h"
    #include "delay.h"
    #include "string.h"
    #include "wifi.h"
    uint8_t Send_wifidata[102];
    
    int main()
    {
    	NVIC_SetPriorityGrouping(5);//两位抢占两位次级
        Usart1_Config(); 
    	SysTick_Config(72000);
    	Esp8266_Config();
        strcpy((char*)Send_wifidata, "hello world");
    	Wifi_ConnectIP();
        U3_SendStr(Send_wifidata);
        while(1)
        {	
        }
    		return 0;
    }
    
    

    WiFi.c

    #include "wifi.h"
    
    WIFIDATA wifidata={0};
    
    //配置串口3  8数据位,0校验位,1停止位,波特率115200
    //PB10(TX) PB11(RX)
    void Esp8266_Config()
    {
    	 //开时钟:GPIOB,USART3
    	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
    	
    	  //配置对应的IO口 PB10(tx):复用推挽 PB11(RX):浮空输入
    	  GPIO_InitTypeDef GPIO_InitStruct = {0};
    		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);
    		//PE6
    		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);
    		
    		
    	  //配置串口3  8数据位,0校验位,1停止位,波特率115200
    		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
    		USART_InitStruct.USART_BaudRate = 115200;//波特率
    		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
    		USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
    		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);
    		USART_Cmd(USART3,ENABLE);
        //配置串口3的中断
    		USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
    		NVIC_SetPriority(USART3_IRQn,7);//设置优先级0~15
    		NVIC_EnableIRQ(USART3_IRQn);//使能中断通道
    		GPIO_SetBits(GPIOE,GPIO_Pin_6);
    	    Delay_nms(500);
    }
    
    void USART3_IRQHandler(void)
    {
    	
    	uint8_t data=0;
    	if((USART3->SR&0x1<<5)!=0)
    	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
    		data = USART_ReceiveData(USART3);//读操作,同时也是清空中断标志位
    		wifidata.recvbuf[wifidata.recvcnt] = data;
    		wifidata.recvcnt++;
    		wifidata.recvcnt%=1024;
    		USART_SendData(USART1, data); 
    	}
    }
    
    //串口5发送单字节函数
    void Usart3Senddata(uint8_t data)
    {
    	//等待发送完成
    	while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==0);
    	//如果上次发送完成,就发送
    	USART_SendData(USART3,data);
    }
    
    //串口5发送数组函数
    void U3_Sendarr(uint8_t * data,uint32_t len)
    {
    	uint32_t i=0;
    	for(i=0;i<len;i++){
    		Usart3Senddata(*data);
    		data++;
    	}
    }
    
    void U3_SendStr(uint8_t * data)
    {	
    	while(*data!='\0')
    	{
    		Usart3Senddata(*data);
    		data++;
    	}
    }
    
    uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
    {
    	uint32_t timecnt=0;
    	memset(&wifidata,0,sizeof(wifidata));
    	U3_SendStr((uint8_t *)cmd);
    	while(strstr((char *)wifidata.recvbuf,recv)==NULL){
    	timecnt++;
    	Delay_nms(1);
    		if(timecnt>=timeout){
    		printf("发送超时失败%s",cmd);
    		return 1;
    	 }
    	}
    	printf(" 发送成功 ");
    	return 0;
    }
    
    uint8_t Wifi_ConnectIP(void)
    {
    	if(Wifi_Send_Cmd("AT\r\n","OK",1000) != 0){//测试
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CWMODE=1\r\n","OK",2000) != 0){//设置为STA
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CWJAP=\"LEGION-5169\",\"88888888\"\r\n","OK",10000)!= 0){//连接热点
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPSTART=\"TCP\",\"36.137.226.30\",37233\r\n","OK",10000)!= 0){//连接服务器
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPMODE=1\r\n","OK",1000)!= 0){//开启透传
    		return 1;
    	}
    	if(Wifi_Send_Cmd("AT+CIPSEND\r\n","OK",1000)!= 0){//启动发送功能
    		return 1;
    	}
    	return 0;	
    }
    
    

    wifi.h

    #ifndef _WIFI_H_
    #define _WIFI_H_
    #include "stm32f10x.h"
    #include "delay.h"
    #include "stdio.h"
    #include "string.h"
    typedef struct{
    	uint8_t recvbuf[1024];
    	uint16_t recvcnt;
    }WIFIDATA;
    
    void Esp8266_Config();
    void U3_SendStr(uint8_t * data);
    uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout);
    uint8_t Wifi_ConnectIP(void);
    void U3_SendStr(uint8_t * data);
    #endif
    		
    

    总结

    1.先看原理图,配串口,引脚和中断。
    2.根据发送逻辑进行中断函数的编写。
    3.在主函数中调用并按照需求进行实现。

    作者:小白橘颂

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32第十六课:WiFi模块的配置及应用

    发表回复