一、准备工作
准备工作见下文第三节:ESP8266+OneNet使用物模型实现数据的收发(仅为连接单片机前的调试工作,本章不涉及单片机)-CSDN博客
二、单片机引脚连接
在使用单片机发送相关AT指令时需要用到单片机的串口功能,下面使用库函数对串口进行配置。小编对USART1和USART3进行了配置,使用串口1连接ESP8266,使用串口3连接USB-TTL(便于将串口1接收到的缓冲通过串口3发送至串口助手观察)。单片机、ESP8266、ST-LINK、USB-TTL引脚连接如下(下图均为引脚名称):
2.1单片机与ST-LINK连接
STM32F103 | ST-LINK |
SWCLK | SWCLK |
SWDIO(或SWIO) | SWDIO |
GND | GND |
3V3(或3.3) | 3V3 |
2.2单片机与ESP8266
STM32F103 | ESP8266 |
PA9 | RX |
PA10 | TX |
GND | G |
3V3(或3.3) | 3V |
2.3单片机与USB-TTL
STM32F103 | USB-TTL |
PB10 | RXD |
由于小编仅使用串口3的TXD,因此不将串口3的RXD与下载器的TXD相连,在下面进行串口配置时小编也不会对串口3的RXD功能进行配置。
三、串口配置
小编使用库函数进行串口的配置,具体见下述代码:
//Serial.c
#include "stm32f10x.h"
uint8_t Serial_RxBuff[300]; //定义接收数据的存放区域
uint16_t RxCount; //定义接收到数据的个数
/**
* @brief 串口1配置
* @param 无
* @retval 无
*/
void Serial_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //打开USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //打开GPIOA的时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9引脚为服用推完输出,即TXD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10引脚为上拉输入,即RXD
USART_InitStructure.USART_BaudRate = 115200; //波特率,可调
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长8位
USART_Init(USART1,&USART_InitStructure); //将结构体参数赋值,配置串口1
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口1接收数据的中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC分组2,2位抢占优先级,2位响应优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //选择配置NVIC的串口线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //配置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //配置响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体参数赋值,配置中断
USART_Cmd(USART1,ENABLE); //使能串口1,串口开始运行
}
/**
* @brief 串口3配置
* @param 无
* @retval 无
*/
void Serial3_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure; //定义结构体变量
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //打开USART3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //打开GPIOB的时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10引脚为服用推完输出,即TXD
USART_InitStructure.USART_BaudRate = 115200; //波特率,可调
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx; //发送模式
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长8位
USART_Init(USART3,&USART_InitStructure); //将结构体参数赋值,配置串口3
USART_Cmd(USART3,ENABLE); //使能串口3,串口开始运行
}
/**
* @brief 串口1发送一个字节
* @param Byte,串口发送的一个字节
* @retval 无
*/
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1,Byte); //将字节写入数据寄存器,写入后自动发送
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //等待发送完成,发送完成自动清除标志位
}
/**
* @brief 串口1发送一个字符串
* @param String,发送字符串的首地址
* @retval
*/
void Serial_SendString(char *String)
{
uint8_t i;
for(i = 0;String[i] != '\0';i++) //遍历字符数组,遇到结束标志停止
{
Serial_SendByte(String[i]); //调用发送一个字节发送数据
}
}
/**
* @brief 串口3发送一个字节
* @param Byte,串口发送的一个字节
* @retval 无
*/
void Serial3_SendByte(uint8_t Byte)
{
USART_SendData(USART3,Byte); //将字节写入数据寄存器,写入后自动发送
while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET); //等待发送完成,发送完成自动清除标志位
}
/**
* @brief 串口1发送一个字符串
* @param String,发送字符串的首地址
* @retval
*/
void Serial3_SendString(char *String)
{
uint8_t i;
for(i = 0;String[i] != '\0';i++) //遍历字符数组,遇到结束标志停止
{
Serial3_SendByte(String[i]); //调用发送一个字节发送数据
}
}
/**
* @brief 清除数据缓冲区
* @param 无
* @retval 无
*/
void Clear_Buffer(void)
{
uint16_t i;
for(i=0;i<RxCount;i++) //将0写入缓冲区,直到大于缓冲区接收到的字符数量
{
Serial_RxBuff[i]=0;//缓存
}
RxCount=0; //重新计数,等待下一次接收
}
/**
* @brief 串口1中断函数
* @param 无
* @retval 无
*/
void USART1_IRQHandler(void)
{
uint8_t data; //定义临时变量存放串口接收的一个字节
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET) //判断是否是串口1的接收事件触发中断
{
data = USART_ReceiveData(USART1); //读取数据寄存器,存放在临时变量
if(data != '\r' || data != '\n' ) //判断接收数据不是结束标志
{
Serial_RxBuff[RxCount++] = data; //不是结束标志,将数据存放在缓存区
}
if(data == '\n') //判断接收数据是结束标志
{
Serial_RxBuff[RxCount] = '\0'; //是结束标志,给缓存区的最后一位添加结束标志
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除标志位
}
}
//Serial.h
#ifndef __SERIAL__H_
#define __SERIAL__H_
extern char Serial_RxBuff[];
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *String);
void Clear_Buffer(void);
void Serial3_Init(void);
void Serial3_SendByte(uint8_t Byte);
void Serial3_SendString(char *String);
#endif
//main.c
#include "stm32f10x.h" // Device header
#include "serial.h"
#include "delay.h"
char CWJAP[] = "AT+CWJAP=\"@PHICOMM_60\",\"lzj123456lzj\"\r\n";
char MQTTUSERCFG[] = "AT+MQTTUSERCFG=0,1,\"esp8266\",\"OWA8D1n1ES\",\"version=2018-10-31&res=products%2FOWA8D1n1ES%2Fdevices%2Fesp8266&et=1745287964&method=md5&sign=VBufe7exEABGp0ub0ezEUQ%3D%3D\",0,0,\"\"\r\n";
char MQTTCONN[] = "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,0\r\n";
int main()
{
Serial_Init();
Serial3_Init();
Serial_SendString("AT+RST\r\n");
Delay_ms(500);
Serial3_SendString(Serial_RxBuff);
Clear_Buffer();
Serial_SendString("AT+CWMODE=1\r\n");
Delay_ms(500);
Serial3_SendString(Serial_RxBuff);
Clear_Buffer();
Serial_SendString(CWJAP);
Delay_ms(3000);
Serial3_SendString(Serial_RxBuff);
Clear_Buffer();
Serial_SendString(MQTTUSERCFG);
Delay_ms(500);
Serial3_SendString(Serial_RxBuff);
Clear_Buffer();
Serial_SendString(MQTTCONN);
Delay_ms(500);
Serial3_SendString(Serial_RxBuff);
Clear_Buffer();
while(1)
{
}
}
在main.c中这里发送的参数就不进行介绍,可以参考本文第一节的准备工作。在这里需要特别注意在发送AT+RST时,正确情况下我们应该接收到ready才判断其复位成功,但在实际操作中小编发现无法完整的将串口1接收到的缓冲通过串口3发送出来。小编猜测在ESP8266的回传数据中有的存在因此无法将其保存在缓冲区(有知道的小伙伴可以分享一下),但所幸并影响后续的操作。其次,还要注意Serial_SendString(CWJAP);需要的时间间隔较长,小编尝试了500ms、1000ms、2000ms在执行后续过程时会返回busy从而导致后续执行出错,知道小编尝试3000ms才成功,因此大家在操作中需要密切注意这一步。
失败:
成功:
同时设备上线
同时再补充一点,如果通过esp8266连接上云平台后,如果再次发送AT+MQTTUSERCFG和AT+MQTTCONN的指令时是会返回ERROE的,必须复位后执行才能正常连接。
代码如下:
【免费】STM32+ESP8266+OneNet(仅连接,不涉及数据的上下行)资源-CSDN文库
补充:将Serial.c的
uint8_t Serial_RxBuff[300]; //定义接收数据的存放区域
uint16_t RxCount; //定义接收到数据的个数
改为
uint8_t Serial_RxBuff[250]; //定义接收数据的存放区域
uint8_t RxCount; //定义接收到数据的个数
可以接收到正常发送AT+RST的回传内容,但会接收到多次