一、stm32的WIFI配置
通常WIFI模块就是一个独立的单片机,只是内置了WFIF通信软件的单片机,并该通信软件提供了AT通信指令集给开发人员,基于这些指令集我们就可以针对项目需要进行二次集成开发出所需的业务应用软件。
本文本文采用的开发板是stm32L496VGT3,该开发板的MCU及WIFI模块(MW31)硬件电路图如下:
MCU采用PC4、PC5连接WIFI实现通信,PB0、PB1作为辅助控制。
WIFI模块给出了两个通信接口,一个用来连接MCU(电路板已经连接好了,本文就采用该接口实现MCP串口通信控制WIFI模块),一个用来给外置使用。
与WIFI通信关联的MCU的IO端口设置要求,开发板给出如下:
以及各个IO口具体参数要求
打开CubeMX配置页面,按上述要求设置WIFI配置通信接口USART3:
开启USART3外部中断功能:
设置PC4、PC5的IO接口参数
同时芯片图形化配置页面设置PB0、PB1为GPIO_OutPut模式,并进入GPIO配置页面,设置PB0、PB1的IO接口参数:
完成配置点击保存按钮或代码生产按钮生成代码:
二、代码设计
本文基于前一篇博文的串口通信能力基础上来实现的,即已经完成了printf函数映射到lpusart进行串口通信:cubeIDE开发, stm32调试信息串口通信输出显示_py_free的博客-CSDN博客
我们先实现串口USART3的接收回调实现,在改写ICore目录下的usart.h、usart.c代码文件:
usart.h内容如下,类似lpusart串口添加了串口USART3的接收辅助变量:
/*
* usart.h
*
* Created on: Oct 20, 2022
* Author: Administrator
*/
#ifndef INC_USART_H_
#define INC_USART_H_
#include "stm32l4xx_hal.h" //HAL库文件声明
#include <string.h>//用于字符串处理的库
#include "print.h"//用于printf函数串口重映射
extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体
extern UART_HandleTypeDef huart3;//声明USART3的HAL库结构体
#define HLPUSART_REC_LEN 256//定义LPUSART最大接收字节数
#define USART3_REC_LEN 256//定义USART3最大接收字节数
extern uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
extern uint16_t HLPUSART_RX_STA;//接收状态标记
extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
extern uint8_t USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
extern uint16_t USART3_RX_STA; //接收状态标记,bit15:接收完成标志,bit14:接收到0x0d,bit11~0:接收到的有效字节数目
extern uint8_t USART3_NewData; //当前串口中断接收的1个字节数据的缓存
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明
#endif /* INC_USART_H_ */
usart.c内容如下,添加了USART3的回调处理,实现接收来自WIFI模块的数据,lpusart和USART3的回调处理几乎一样的,主要就是接收到一个数据,就缓存起来,并再次开启中断接收;在接收到回车字段或缓存溢出是,进行标记(HLPUSART_RX_STA和USART3_RX_STA的前四位可以标记,后12位为数据长度记录)改写,该标记留给外部调用来处理,不再回调函数中进行接收标记复位:
/*
* usart.c
*
* Created on: Oct 20, 2022
* Author: Administrator
*/
#include "usart.h"
#include "wifi.h"
uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
/*
* bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
* bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
* bit13:预留
* bit12:预留
* bit11~0:接收到的有效字节数目(0~4095)
*/
uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
uint8_t USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART3_RX_STA=0; //接收状态标记,bit15:接收完成标志,bit14:接收到0x0d,bit11~0:接收到的有效字节数目
uint8_t USART3_NewData; //当前串口中断接收的1个字节数据的缓存
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
{
if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
{
if(HLPUSART_NewData==0x0d){//回车标记
HLPUSART_RX_STA|=0x8000;//标记接到回车
}else{
if((HLPUSART_RX_STA&0X0FFF)<HLPUSART_REC_LEN){
HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
HLPUSART_RX_STA++; //数据长度计数加1
}else{
HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
}
}
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
}
if(huart ==&huart3)//判断中断来源(串口3:WIFI模块)
{
if(USART3_NewData==0x0d){//回车标记
USART3_RX_STA|=0x8000;//标记接到回车
}else{
if((USART3_RX_STA&0X0FFF)<USART3_REC_LEN){
USART3_RX_BUF[USART3_RX_STA&0X0FFF]=USART3_NewData; //将收到的数据放入数组
USART3_RX_STA++; //数据长度计数加1
}else{
USART3_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
}
}
HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启接收中断
}
}
在ICore目录下创建wifi.h和wifi.c源码文件实现WIFI驱动:
wifi.h内容如下:
/*
* wifi.h
*
* Created on:
* Author: Administrator
*/
#ifndef WIFI_WIFI_H_
#define WIFI_WIFI_H_
#include "stm32l4xx_hal.h" //HAL库文件声明
#include "usart.h"
#include "main.h"
#include <string.h>//用于字符串处理的库
#include <stdarg.h>
#include <stdlib.h>
#include "stdio.h"
extern UART_HandleTypeDef huart3;//声明UART2的HAL库结构体
void WIFI_printf (char *fmt, ...); //WIFI模块发送
void WIFI_TCP_SEND (char *fmt, ...);//在TCP模式下的发送数据(不处理返回状态的盲发)
#endif /* WIFI_WIFI_H_ */
wifi.c内容如下,本质上就是调用HAL_UART_Transmit函数对huart3句柄进行写入数据操作,实现发送数据到WIFI模块:
/*
* wifi.c
*
* Created on:
* Author: Administrator
*/
#include "wifi.h"
//WIFI模块通信,使用UART3,这是专用的printf函数
//调用方法:WIFI_printf("123"); //向USART2发送字符123
void WIFI_printf (char *fmt, ...)
{
char buff[USART3_REC_LEN+1]; //用于存放转换后的数据 [长度]
uint16_t i=0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART3_REC_LEN+1, fmt, arg_ptr);//数据转换
i=strlen(buff);//得出数据长度
if(strlen(buff)>USART3_REC_LEN)i=USART3_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略)
/*HAL_StatusTypeDef ret =*/ HAL_UART_Transmit(&huart3,(uint8_t *)buff,i,0xffff);//串口发送函数(串口号,内容,数量,溢出时间)
//printf("WIFI_printf Len:%02d\r\n",ret);
va_end(arg_ptr);
}
//WIFI模块在TCP模式下的数据发送:TCP发送的规定是先发AT+CIPSEND=数量,等待返回“>“后再发送数据内容。
//调用方法:WIFI_TCP_SEND("123\r\n"); //TCP方式发送字符123和回车换行
void WIFI_TCP_SEND (char *fmt, ...)
{
char buff[USART3_REC_LEN+1]; //用于存放转换后的数据 [长度]
uint16_t i=0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART3_REC_LEN+1, fmt, arg_ptr);//数据转换
i=strlen(buff);//得出数据长度
if(strlen(buff)>USART3_REC_LEN)i=USART3_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略)
WIFI_printf("AT+CIPSEND=%d\r\n",i);//先发送AT指令和数据数量
HAL_Delay(100);//等待WIFI模块返回">",此处没做返回是不是">"的判断。稳定性要求高的项目要另加判断。
HAL_UART_Transmit(&huart3,(uint8_t *)buff,i,0xffff);//发送数据内容(串口号,内容,数量,溢出时间)
va_end(arg_ptr);
}
//所有USART串口的中断回调函数HAL_UART_RxCpltCallback,统一存放在【usart.C】文件中。
在main.c文件添加WIFI通信实现代码,main.c部分内容如下:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//用户编写1
#include "../../ICore/key.h"
#include "../../ICore/led.h"
#include "../../ICore/print.h"
#include "../../ICore/usart.h" //添加串口usart3接收回调实现的头文件
#include "../../ICore/wifi.h" //添加串口usart3写入数据实现的头文件
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
//用户编写2
ResetPrintInit(&hlpuart1);
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
HLPUSART_RX_STA = 0;
HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData, 1); //usart3开启接收中断
USART3_RX_STA = 0;
set_led0_val(0);
set_led1_val(get_key0_val());
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//用户编写3
//接收到电脑发送给(lpusart)数据,转身将其发送到给usart3,进而实现送数据给WIFI模块
if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
WIFI_printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
HLPUSART_RX_STA=0;//接收错误,重新开始
HAL_Delay(100);//等待
}
//接收到WIFI模块发送给(usart3)数据,转身将其发送到给lpusart,进而实现送数据给电脑终端
if(USART3_RX_STA&0xC000){//溢出或换行,重新开始
printf("%.*s\r\n", USART3_RX_STA&0X0FFF, USART3_RX_BUF);
USART3_RX_STA=0;//接收错误,重新开始
HAL_Delay(100);//等待
}
/*
if(KEY_0())//按下KEY0判断
{
printf("KEY0被按下\r\n");
WIFI_printf("AT+UARTE?\r\n");//发送AT指令
HAL_Delay(100);//等待
}
*/
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
三、编译及测试
点击编译及运行按钮,
如果编译及下载无异常,采用串口助手连接开发板,本文将开发板作为Station 模式连接家里的路由,设置账号密码及查询是否成功:
测试运行结果如下:
访问家里的路由控制界面查看如下:
四、关于WIFI模块的串口应用
通过上面的实现,市面上大部分WIFI模块都能适用。只要AT指令能直达WIFI模块,无论是从MCU还是从电脑端,对于WIFI模块的操作就是看AT指令支持的情况及我们对AT指令的组合应用,可以实现项目开发中各种对WIFI使用的要求。当然在具体项目中,对于WIFI模块的选型就是看他们WIFI模块给的AT指令能否直接或间接组合满足项目要求。