STM32---项目学习日记
1.OLED
现象:OLED左上角第一列会完全点亮(8 个像素全亮)
(1)oled.c
#include "oled.h"
#include "oledfont.h"
extern I2C_HandleTypeDef hi2c1;
//初始化命令
uint8_t CMD_Data[]={
0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F,
0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12,
0xD8, 0x30, 0x8D, 0x14, 0xAF};
void WriteCmd()
{
uint8_t i = 0;
for(i = 0; i < 27; i++){
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, CMD_Data+i, 1, 0x100);
}
}
//向设备写控制命令
void OLED_WR_CMD(uint8_t cmd)
{
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 0x100);
}
//向设备写数据
void OLED_WR_DATA(uint8_t data)
{
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &data, 1, 0x100);
}
//初始化oled屏幕
void OLED_Init(void)
{
HAL_Delay(200);
WriteCmd();
}
//清屏size12 size16要清两行,其他函数有类似情况
void OLED_Clear()
{
uint8_t i, n;
for(i = 0; i < 8; i++)
{
OLED_WR_CMD(0xb0 + i);
OLED_WR_CMD (0x00);
OLED_WR_CMD (0x10);
for(n = 0; n < 128; n++)
OLED_WR_DATA(0);
}
}
//清行
void OLED_Clearrow(uint8_t i)
{
uint8_t n;
OLED_WR_CMD(0xb0 + i);
OLED_WR_CMD (0x00);
OLED_WR_CMD (0x10);
for(n = 0; n < 128; n++)
OLED_WR_DATA(0);
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_CMD(0X8D); //SET DCDC命令
OLED_WR_CMD(0X14); //DCDC ON
OLED_WR_CMD(0XAF); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_CMD(0X8D); //SET DCDC命令
OLED_WR_CMD(0X10); //DCDC OFF
OLED_WR_CMD(0XAE); //DISPLAY OFF
}
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
OLED_WR_CMD(0xb0 + y);
OLED_WR_CMD(((x & 0xf0) >> 4) | 0x10);
OLED_WR_CMD(x & 0x0f);
}
void OLED_On(void)
{
uint8_t i, n;
for(i = 0; i < 8; i++)
{
OLED_WR_CMD(0xb0 + i); //设置页地址(0~7)
OLED_WR_CMD(0x00); //设置显示位置—列低地址
OLED_WR_CMD(0x10); //设置显示位置—列高地址
for(n = 0; n < 128; n++)
OLED_WR_DATA(1);
} //更新显示
}
unsigned int oled_pow(uint8_t m, uint8_t n)
{
unsigned int result = 1;
while(n--)result *= m;
return result;
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
unsigned char c = 0, i = 0;
c = chr - ' ';//得到偏移后的值
if(x > 128 - 1)
{
x = 0;
y = y + 2;
}
if (Char_Size == 40)
{
OLED_Set_Pos(x, y);
for (i = 0; i < 20; i++)
OLED_WR_DATA(F20X40[c * 100 + i]);
OLED_Set_Pos(x, y + 1);
for (i = 0; i < 20; i++)
OLED_WR_DATA(F20X40[c * 100 + i + 20]);
OLED_Set_Pos(x, y + 2);
for (i = 0; i < 20; i++)
OLED_WR_DATA(F20X40[c * 100 + i + 40]);
OLED_Set_Pos(x, y + 3);
for (i = 0; i < 20; i++)
OLED_WR_DATA(F20X40[c * 100 + i + 60]);
OLED_Set_Pos(x, y + 4);
for (i = 0; i < 20; i++)
OLED_WR_DATA(F20X40[c * 100 + i + 80]);
}
else if (Char_Size == 32)
{
OLED_Set_Pos(x, y);
for (i = 0; i < 16; i++)
OLED_WR_DATA(F16X32[c * 64 + i]);
OLED_Set_Pos(x, y + 1);
for (i = 0; i < 16; i++)
OLED_WR_DATA(F16X32[c * 64 + i + 16]);
OLED_Set_Pos(x, y + 2);
for (i = 0; i < 16; i++)
OLED_WR_DATA(F16X32[c * 64 + i + 32]);
OLED_Set_Pos(x, y + 3);
for (i = 0; i < 16; i++)
OLED_WR_DATA(F16X32[c * 64 + i + 48]);
}
else if(Char_Size == 16)
{
OLED_Set_Pos(x,y);
for(i=0;i<8;i++)
OLED_WR_DATA(F8x16[c * 16 + i]);
OLED_Set_Pos(x, y + 1);
for(i = 0; i < 8;i++)
OLED_WR_DATA(F8x16[c*16+i+8]);
}
else
{
OLED_Set_Pos(x, y);
for(i = 0; i < 6; i++)
OLED_WR_DATA(F6x8[c][i]);
}
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2)
{
uint8_t t, temp;
uint8_t enshow = 0;
for(t = 0; t < len; t++)
{
temp = (num / oled_pow(10, len - t - 1)) % 10;
if(enshow == 0 && t < (len - 1))
{
if(temp == 0)
{
OLED_ShowChar(x + (size2 / 2) * t, y, ' ', size2);
continue;
}
else
enshow=1;
}
OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2);
}
}
//显示一个字符号串
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size)
{
unsigned char j = 0;
while (chr[j] != '\0')
{
OLED_ShowChar(x, y, chr[j], Char_Size);
x += 8;
if(x > 120)
{
x = 0;
y += 2;
}
j++;
}
}
//显示汉字
//hzk 用取模软件得出的数组
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t no)
{
uint8_t t, adder = 0;
OLED_Set_Pos(x, y);
for(t = 0; t < 16; t++)
{
OLED_WR_DATA(Hzk[2 * no][t]);
adder += 1;
}
OLED_Set_Pos(x,y+1);
for(t = 0; t < 16; t++)
{
OLED_WR_DATA(Hzk[2 * no + 1][t]);
adder += 1;
}
}
void oled_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *pic)
{
uint8_t temp_page = 0;
uint8_t start_page = y0 / 8;
uint8_t end_page = y1 / 8;
for (temp_page = start_page; temp_page < end_page; temp_page++)
{
OLED_Set_Pos(x0, temp_page);
for(int t = 0; t < 128; t++)
{
OLED_WR_DATA(*(pic++));
}
x0 = 0;
}
}
(2)main.c
OLED_Init();
OLED_Clear();
//点亮第一列
uint8_t Data;
Data = 0xb0;
HAL_I2C_Mem_Write(&hi2c1, 0x78,0x00,I2C_MEMADD_SIZE_8BIT,&Data,1,HAL_MAX_DELAY);//page0
Data = 0x00;//低四位
HAL_I2C_Mem_Write(&hi2c1, 0x78,0x00,I2C_MEMADD_SIZE_8BIT,&Data,1,HAL_MAX_DELAY);//page0
Data = 0x10;//高四位
HAL_I2C_Mem_Write(&hi2c1, 0x78,0x00,I2C_MEMADD_SIZE_8BIT,&Data,1,HAL_MAX_DELAY);//page0
Data = 0xff;
HAL_I2C_Mem_Write(&hi2c1, 0x78,0x40,I2C_MEMADD_SIZE_8BIT,&Data,1,HAL_MAX_DELAY);//page0
(3)现象
2.OLED显示字符或汉字
(1)oled.c
if(Char_Size == 64)
{
c = chr - '0';
OLED_Set_Pos(x,y);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i]);
OLED_Set_Pos(x,y+1);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 32]);
OLED_Set_Pos(x,y+2);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 64]);
OLED_Set_Pos(x,y+3);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 96]);
OLED_Set_Pos(x,y+4);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 128]);
OLED_Set_Pos(x,y+5);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 160]);
OLED_Set_Pos(x,y+6);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 192]);
OLED_Set_Pos(x,y+7);
for(i=0;i<32;i++)
OLED_WR_DATA(F32X64[c * 256 + i + 224]);
}
2.BH1750
STM32 + I2C 驱动 BH1750 数字光照传感器
- 向 BH1750 发送测量指令
- 读取传感器返回的 2 字节原始数据
- 将数据转换成光照强度(单位 lx)
(1)bh1750.c
#include"bh1750.h"
extern I2C_HandleTypeDef hi2c1;
功能:向 BH1750 发送一个指令(如启动测量)
HAL_I2C_Master_Transmit:STM32 作为主机发送数据&hi2c1:使用 I2C1BH1750_READ_ADDR(0x47):传感器的 I2C 地址&cmd:要发送的指令1:发送 1 个字节HAL_MAX_DELAY:无限等待直到完成
//写命令
void BH1750_Send_CMD(uint8_t cmd)
{
HAL_I2C_Master_Transmit(&hi2c1,BH1750_READ_ADDR,&cmd,1,HAL_MAX_DELAY);
}
//读结果
void BH1750_Read_Data(uint8_t *pData)
{
HAL_I2C_Master_Receive(&hi2c1,BH1750_READ_ADDR,pData,2,HAL_MAX_DELAY);
}
功能:读取 BH1750 返回的 2 字节光照原始数据
HAL_I2C_Master_Receive:主机接收数据pData:传入数组指针,用来存放读到的 2 个字节2:读取 2 个字节(BH1750 输出固定 16 位数据)
//转换结果
uint16_t BH1750_Data_To_Lx(uint8_t *pData)
{
uint16_t light = pData[0];
light <<= 8;
light += pData[1];
light = (uint16_t)(light / 1.2);
return light;
}
功能:把传感器的 2 字节原始数据 → 转换成光照强度 lx
- BH1750 输出高字节在前,低字节在后
- 必须移位合并:
高8位 <<8 | 低8位 - 官方公式:光照值 = 合并值 / 1.2
(2)bh1750.h
#ifndef __BH1750_H_
#define __BH1750_H_
#include"stm32f4xx_hal.h"
#define BH1750_WRITE_ADDR 0X46
#define BH1750_READ_ADDR 0X47
void BH1750_Read_Data(uint8_t *pData);
void BH1750_Send_CMD(uint8_t cmd);
uint16_t BH1750_Data_To_Lx(uint8_t *pData);
#endif
(3)main.c部分
uint8_t Data[2] = {0};
while (1)
{
BH1750_Send_CMD(0X20);// 发送指令:连续高分辨率模式
HAL_Delay(150);
BH1750_Read_Data(Data);
printf("--- %d ---\r\n",BH1750_Data_To_Lx(Data));
HAL_Delay(1000);
指令 0x20 含义
- 连续测量模式 / 高分辨率模式
- 分辨率 1lx
- 转换时间典型值:120ms
整套代码的执行流程
- 循环发送0x20 测量指令
- 等待150ms让传感器完成采集
- 读取2 字节原始数据
- 将高 8 位 + 低 8 位合并成 16 位数据
- 除以1.2得到最终光照强度
- 串口打印结果
- 延时 1 秒,重复执行
3.DHT11
这是一段基于 STM32 HAL 库编写的DHT11 温湿度传感器驱动代码,采用单总线协议通信,包含微秒级延时、传感器初始化、起始信号、应答检测、数据读取等完整功能。
#include"dht11.h"
void Delay_US(uint32_t us)
{
uint32_t old_time = SysTick->VAL;
uint32_t new_time;
uint32_t ticks = us * 100;
uint32_t cnt = 0;
uint32_t load = SysTick->LOAD;
while(1)
{
new_time = SysTick->VAL;
if(new_time != old_time)
{
if(new_time < old_time)
cnt += (old_time - new_time);
else
cnt += (load - new_time + old_time);
if(cnt >= ticks)
break;
old_time = new_time;
}
}
}
功能
实现精准微秒级延时,DHT11 单总线协议严格依赖时序,必须用微秒延时。
原理
- 读取系统滴答定时器
SysTick的当前计数值、重装载值; - 计算需要延时的总时钟周期数
ticks = us*100; - 循环读取定时器值,统计经过的时钟周期,达到设定值后退出循环,完成延时。
作用
为 DHT11 通信时序提供us 级延时(如 30us、1us)
void DHT11_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_Init_Struct;
GPIO_Init_Struct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init_Struct.Pin = GPIO_PIN_3;
GPIO_Init_Struct.Pull = GPIO_PULLUP;
GPIO_Init_Struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_Init_Struct);
}
功能
初始化PB3 引脚为 DHT11 通信引脚。
关键配置解析
__HAL_RCC_GPIOB_CLK_ENABLE():开启 GPIOB 时钟,GPIO 必须先开时钟才能使用;GPIO_MODE_OUTPUT_OD:开漏输出模式(单总线协议标准配置,支持双向通信);GPIO_PULLUP:内部上拉,保证总线空闲时为高电平;GPIO_SPEED_FREQ_HIGH:高速 GPIO,适配传感器通信时序。
作用
让 PB3 引脚具备与 DHT11 通信的硬件条件。
void DHT11_Start(void)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_SET);
Delay_US(30);
}
功能
向 DHT11 发送单总线起始信号,唤醒传感器。
时序逻辑(DHT11 标准协议)
- 主机(STM32)拉低总线至少 18ms(代码用 20ms);
- 主机释放总线,拉高电平;
- 延时 30us,等待传感器响应。
作用
唤醒 DHT11,告诉传感器:准备发送数据
uint8_t DHT11_Response(void)
{
uint16_t time = 0;
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)&&time<100)
{
Delay_US(1);
time++;
}
if(time>=100)
return 1;
time = 0;
while(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)&&time<100)
{
Delay_US(1);
time++;
}
if(time>= 100)
return 1;
return 0;
}
功能
检测 DHT11 是否正常响应起始信号,判断传感器是否在线。
应答逻辑(DHT11 标准协议)
- 等待总线拉低(传感器拉低总线表示开始响应),超时 100us 则判定失败;
- 等待总线拉高(传感器完成应答),超时 100us 则判定失败;
- 无超时:返回
0(传感器正常);超时:返回1(传感器异常 / 未连接)。
作用
确认 DHT11 已被成功唤醒,可以开始传输数据。
uint8_t DHT11_Read_Bit(void)
{
uint16_t time = 0;
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)&&time<100)
{
Delay_US(1);
time++;
}
if(time>=100)
return 2;
while(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)&&time<100)
{
Delay_US(1);
time++;
}
if(time>= 100)
return 2;
Delay_US(30);
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3) == 0)
return 0;
else
return 1;
}
功能
从 DHT11 读取1bit(一位)数据,是读取字节的基础。
位数据读取原理
- 等待传感器拉低总线(数据传输开始);
- 等待传感器拉高总线;
- 延时 30us 后读取电平:
- 低电平 → 数据
0 - 高电平 → 数据
1
- 低电平 → 数据
- 超时返回
2,表示读取失败。
作用
单次读取 1 位二进制数据。
uint8_t DHT11_Read_Byte(void)
{
uint8_t data = 0;
uint8_t i = 0;
for (i=0;i<8;i++)
{
data <<= 1;
data = data | DHT11_Read_Bit();
}
return data;
}
功能
连续读取8 次 1bit 数据,拼接成1 字节(8bit)数据。
逻辑解析
data <<= 1:数据左移一位,腾出最低位;data | DHT11_Read_Bit():将新读取的 1bit 写入最低位;- 循环 8 次,得到完整的 1 字节数据。
作用
读取 DHT11 传输的单字节数据。
void DHT11_Read_Data(uint8_t *pData)
{
DHT11_Start();
if(DHT11_Response())
return;
uint8_t i;
for(i=0;i<5;i++)
{
pData[i] = DHT11_Read_Byte();
}
if(pData[4] != pData[0] + pData[1] + pData[2] + pData[3])
{
for(i=0;i<5;i++)
{
pData[i] = 0;
}
}
}
功能
读取 DHT11 完整的5 字节数据,并进行校验。
5 字节数据定义(DHT11 标准)
pData[0]:湿度整数部分pData[1]:湿度小数部分(DHT11 固定为 0)pData[2]:温度整数部分pData[3]:温度小数部分(DHT11 固定为 0)pData[4]:校验和= 前 4 字节之和
/* USER CODE BEGIN 1 */
int fputc(int c,FILE *p)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,HAL_MAX_DELAY);
return c;
}
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
DHT11_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint8_t res[5] = {0};
while (1)
{
memset(res,0,5);
DHT11_Read_Data(res);
printf("humi: %d.%d temp: %d.%d\r\n",res[0],res[1],res[2],res[3]);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
#ifndef DHT11_H
#define DHT11_H
#include "stm32f4xx_hal.h"
void DHT11_Init(void);
void DHT11_Start(void);
void DHT11_Read_Data(uint8_t *pData);
#endif
整体总结
- 通信方式:单总线协议,仅用PB3一个引脚完成通信;
- 数据格式:5 字节数据(湿度整、湿度小、温度整、温度小、校验和);
- 流程:初始化 → 起始信号 → 应答检测 → 逐位读数据 → 拼接字节 → 数据校验;
- 核心依赖:
Delay_US微秒延时,保证严格的 DHT11 通信时序; - 使用方式:调用
DHT11_Read_Data传入数组,即可获取温湿度原始值。
4.ESP8266
这份代码是STM32 通过串口(UART2)驱动 ESP8266 WiFi 模块的底层驱动,包含模块复位、串口响应检测、串口波特率修改三个核心功能
#include"esp8266.h"
#include"stdio.h"
#include"string.h"
extern UART_HandleTypeDef huart2;
extern uint8_t g_UART_Buf[256];
extern uint16_t g_Index;
void ESP8266_Init(void)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
HAL_Delay(20);
}
功能:
硬件复位 ESP8266 模块
- 原理:ESP8266 的复位脚(接在 STM32 的 PB12)低电平复位,高电平工作
- 流程:拉低复位脚 → 延时保持复位 → 拉高复位脚 → 延时等待模块启动
- 作用:每次初始化时让 WiFi 模块恢复初始状态,避免异常
void ESP8266_Response(const char *msg,uint32_t timeout)
{
uint32_t Cur_Tick = HAL_GetTick();
uint8_t data,i = 0;
while(1)
{
if(HAL_GetTick() - Cur_Tick >= timeout)//超时
break;
if(HAL_UART_Receive(&huart2,&data,1,10) == HAL_TIMEOUT)
continue;
//printf("%c",data);
g_UART_Buf[g_Index++] = data;//把数据保存到缓冲区
if(data == msg[i])
{
i++;
if(i == strlen(msg))
break;
}
else
{
i = 0;
}
}
}
核心功能:
- 参数
const char *msg:要检测的目标字符串(如"OK")uint32_t timeout:最大等待超时时间(单位:ms)
- 工作流程
- 计时开始 → 循环读取串口 1 字节数据 → 存入全局缓冲区
- 逐字符对比目标字符串,匹配成功则继续,失败则重置匹配
- 两种退出条件:完整匹配到目标字符串或等待超时
- 用途:发送 AT 指令后,调用这个函数检测 ESP8266 是否返回预期结果
void ESP8266_SetBaud(uint32_t baud)//重新改的比特率
{
huart2.Instance = USART2;
huart2.Init.BaudRate = baud;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
}
功能:
动态修改 STM32 串口 2 的波特率
- 背景:ESP8266 默认波特率通常是 115200,可通过 AT 指令修改模块波特率
- 作用:修改 ESP8266 波特率后,必须同步修改 STM32 串口波特率,才能正常通信
- 特点:完整重配置串口所有参数,最后调用
HAL_UART_Init生效
#ifndef ESP8266_H
#define ESP8266_H
#include"stm32f4xx_hal.h"
void ESP8266_Response(const char *msg,uint32_t timeout);
void ESP8266_SetBaud(uint32_t baud);
void ESP8266_Init(void);
#endif
int fputc(int c,FILE *p)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,HAL_MAX_DELAY);
return c;
}
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
ESP8266_Init();
ESP8266_Response("csum 0xde\r\n",2000);
ESP8266_SetBaud( 115200);
ESP8266_Response("ready\r\n",1000);
ESP8266_Response("xxxx",1000);
printf("%s",g_UART_Buf);
g_Index = 0;
/* USER CODE END 2 */
/* Infinite loop */
整体代码总结
核心功能
- ESP8266_Init:硬件复位 WiFi 模块,初始化模块状态
- ESP8266_Response:串口接收 + 字符串匹配,检测模块返回的指令响应
- ESP8266_SetBaud:动态修改串口波特率,适配 ESP8266 的通信速率
代码用途
这是 STM32 驱动 ESP8266 的底层基础函数,上层可以基于这三个函数,实现发送 AT 指令、连接 WiFi、联网通信等功能
6.ESP8266进阶
整体功能概述
这是一份STM32 驱动 ESP8266 连接 WIFI + MQTT 服务器的固件代码,基于 HAL 库,通过串口(USART2)与 ESP8266 通信,实现:
- ESP8266 初始化
- 连接路由器 WIFI
- 连接 MQTT 服务器
- MQTT 消息发布(Publish)
- MQTT 主题订阅(Subscribe)
- 串口接收、指令等待响应、缓冲区管理
include"esp8266.h"
#include"stdio.h"
#include"string.h"
extern UART_HandleTypeDef huart2;
extern uint8_t g_UART_Buf[256];
extern uint16_t g_Index;
void ESP8266_Init(void)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
HAL_Delay(20);
ESP8266_Response("csum 0xde\r\n",2000);
ESP8266_SetBaud( 115200);
ESP8266_Response("ready\r\n",1000);
printf("%s",g_UART_Buf);
g_Index = 0;
memset(g_UART_Buf,0,512);
}
uint8_t ESP8266_Response(const char *msg,uint32_t timeout)
{
uint32_t Cur_Tick = HAL_GetTick();
uint8_t data,i = 0;
while(1)
{
if(HAL_GetTick() - Cur_Tick >= timeout)//超时
return 0;
if(HAL_UART_Receive(&huart2,&data,1,10) == HAL_TIMEOUT)
continue;
//printf("%c",data);
g_UART_Buf[g_Index++] = data;//把数据保存到缓冲区
if(data == msg[i])
{
i++;
if(i == strlen(msg))
return 1;
}
else
{
i = 0;
}
}
}
void ESP8266_SetBaud(uint32_t baud)//重新改的比特率
{
huart2.Instance = USART2;
huart2.Init.BaudRate = baud;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
}
void ESP8266_Send_Cmd(const char *cmd)//封装函数,发送命令
{
HAL_UART_Transmit(&huart2,(uint8_t *)cmd,strlen(cmd),HAL_MAX_DELAY);
HAL_UART_Transmit(&huart2,(uint8_t *)"\r\n",2,HAL_MAX_DELAY);
}
void ESP8266_Refresh_Buffer()
{
printf("%s\r\n",g_UART_Buf);
g_Index = 0;
memset(g_UART_Buf,0,512);
}
void ESP8266_Connect_AP(const char *ssid,const char *passwd)// WIFI名字和密码
{
char cmd[128] = {0};
sprintf(cmd,"AT+CWJAP=\"%s\",\"%s\"",ssid,passwd);//转义字符
ESP8266_Send_Cmd(cmd);
if(ESP8266_Response("OK",50000))//20s内连上
{
ESP8266_Refresh_Buffer();
}
else
{
ESP8266_Refresh_Buffer();
printf("CONNECT WIFI FAILURE\r\n ");
}
}
void ESP8266_Connect_MQTT(const char *ip,int port,const char *user,const char *passwd)
{
char cmd[128] = {0};
sprintf(cmd,"AT+MQTTUSERCFG=0,1,\"0001\",\"%s\",\"%s\",0,0,\" \"",user,passwd);//Set MQTT User Config
ESP8266_Send_Cmd(cmd);
if(ESP8266_Response("OK",5000))
{
ESP8266_Refresh_Buffer();
}
else
{
ESP8266_Refresh_Buffer();
printf("CONFIG MQTT FAILURE\r\n");
}
memset(cmd,0,sizeof(cmd));
sprintf(cmd,"AT+MQTTCONN=0,\"%s\",%d,1",ip,port); //Connect to a MQTT broker主机
ESP8266_Send_Cmd(cmd);
if(ESP8266_Response("OK",10000))
{
ESP8266_Refresh_Buffer();
}
else
{
ESP8266_Refresh_Buffer();
printf("CONNECT MQTT FAILURE\r\n");
}
}
AT+MQTTUSERCFG→ 配置 MQTT 账号AT+MQTTCONN→ 连接服务器(IP + 端口)
void ESP8266_Publish(const char *topic,const char *msg)//发布 no 订阅
{
char cmd[256] = {0};
sprintf(cmd,"AT+MQTTPUB=0,\"%s\",\"%s\",0,0",topic,msg);
ESP8266_Send_Cmd(cmd);
if(ESP8266_Response("OK",10000))
{
ESP8266_Refresh_Buffer();
}
else
{
ESP8266_Refresh_Buffer();
printf("PUBLISH FAILURE\r\n");
}
}
指令:AT+MQTTPUB功能:向指定主题发送消息
void ESP8266_Subscribe(const char *topic)
{
char cmd[128] = {0};
sprintf(cmd,"AT+MQTTSUB=0,\"%s\",0",topic);
ESP8266_Send_Cmd(cmd);
if(ESP8266_Response("OK",10000))
{
ESP8266_Refresh_Buffer();
}
else
{
ESP8266_Refresh_Buffer();
printf("SUBSCRIBE FAILURE\r\n");
}
}
指令:AT+MQTTSUB功能:订阅主题,等待服务器下发消息
#ifndef ESP8266_H
#define ESP8266_H
#include"stm32f4xx_hal.h"
uint8_t ESP8266_Response(const char *msg,uint32_t timeout);
void ESP8266_SetBaud(uint32_t baud);
void ESP8266_Init(void);
void ESP8266_Connect_AP(const char *ssid,const char *passwd);// WIFI名字和密码
void ESP8266_Connect_MQTT(const char *ip,int port,const char *user,const char *passwd);//
void ESP8266_Publish(const char *topic,const char *msg);//发布
void ESP8266_Subscribe(const char *topic);//订阅
void ESP8266_Refresh_Buffer(void);
#endif
uint8_t data,Flag = 0;
uint32_t old_tick = 0;
while (1)
{
if(HAL_GetTick() - old_tick > 50 && Flag )
{
ESP8266_Refresh_Buffer();
Flag = 0;
}
if(HAL_UART_Receive(&huart2,&data,1,10) == HAL_TIMEOUT)//超时
{
continue;
}
else
{
g_UART_Buf[g_Index++] = data;
old_tick = HAL_GetTick();
Flag = 1;
}
代码整体逻辑流程图
- 复位 ESP8266
- 初始化串口波特率 115200
- 连接 WIFI
- 配置 MQTT 用户信息
- 连接 MQTT 服务器
- 可发布 / 订阅 MQTT 消息
- 所有指令都靠
ESP8266_Response等待OK判断成功
关键特点总结
- 基于ESP8266 AT 指令集 MQTT
- 用状态匹配方式判断指令是否成功
- 缓冲区全局共享
- 超时机制保证不卡死
- 封装清晰:初始化 → WIFI → MQTT → 发布 → 订阅
