一、系统方案介绍
随着物联网技术的快速发展,传统厨房设备正向智能化、安全化方向升级。本文设计了一款基于STM32F103C8T6单片机的智能灶台控制系统。该系统具备倒计时定时关闭、多档火力调节、烟雾/温度实时监测、电机模拟火力大小等核心功能,可有效防止因燃气泄漏、温度过高引发的安全事故,提升烹饪体验。
系统采用模块化设计,以STM32F103C8T6为主控芯片,外接0.96寸OLED显示屏、6个独立按键、MQ2烟雾传感器、DS18B20温度传感器、直流电机(带驱动器)及4个状态指示灯。用户通过按键可方便地设置倒计时、开关灶台、调节火力等级;同时传感器实时监测环境参数,一旦烟雾浓度或温度超过阈值,系统自动切断灶台工作,实现主动安全保护。
系统亮点:
-
双重安全保障:烟雾+温度传感器联合监测,超阈值自动关闭
-
人性化交互:OLED实时显示火力档位、倒计时、传感器数值
-
可视化反馈:4个LED分别指示开关状态和高中低三档火力
-
模拟执行机构:电机转速随火力线性变化,直观演示火力大小

二、系统架构图

三、实现方法
3.1 开发环境
-
IDE:Keil MDK 5.32
-
库函数:STM32标准外设库
-
调试工具:ST-Link V2
-
编程语言:C语言
3.2 实现流程
-
初始化系统:时钟配置(72MHz)、GPIO、ADC、定时器PWM、I2C、单总线。
-
显示界面:OLED显示初始状态(火力关闭,烟雾/温度数值,倒计时00:00)。
-
按键扫描:循环检测6个按键动作,执行对应功能(带软件消抖)。
-
传感器采集:每200ms读取一次烟雾ADC值和DS18B20温度值。
-
安全判断:若烟雾值 > 阈值 或 温度 > 阈值 → 关闭灶台(`stove_on = 0`,电机停转,LED1灭)。
-
倒计时处理:倒计时模式下,每秒递减,减至0时自动关闭灶台。
-
火力输出:根据当前火力等级(0/1/2/3对应关闭/低/中/高)设置PWM占空比,控制电机转速。
3.3 核心算法
-
按键消抖:延时20ms后再次读取确认。
-
PWM调速:低档对应20%占空比,中档50%,高档90%。
-
传感器阈值:烟雾阈值设定为ADC值400(约0.8V,对应较高浓度),温度阈值60℃。
四、功能描述
|----------|---------------|---------------------------------|
| 功能模块 | 实现方式 | 用户操作/现象 |
| 灶台开关 | 按键4切换开关标志 | LED1亮表示开,灭表示关;火力加减仅在开状态下有效 |
| 火力调节 | 按键5增加,按键6减少 | 循环选择高/中/低三档;LED2/3/4分别对应高/中/低点亮 |
| 电机模拟火力 | PWM驱动电机 | 低档慢转,中档中速,高档快速旋转 |
| 倒计时模式 | 按键1进入,按键2/3加减 | 可设1~99分钟;倒计时结束自动关闭灶台 |
| 烟雾检测 | MQ2连续采样 | 超过阈值立即关灶台,OLED显示"Smoke Alert!" |
| 温度检测 | DS18B20测量环境温度 | 超过60℃关灶台,显示"Over Temp!" |
| 状态显示 | OLED 128x64 | 实时显示火力档位、倒计时剩余、烟雾值、温度值 |
> 注意:倒计时模式下,若因烟雾/温度超限关闭灶台,倒计时将重置。
五、硬件架构
5.1 主控芯片
STM32F103C8T6
-
内核:ARM Cortex-M3,最高72MHz
-
存储:64KB Flash,20KB RAM
-
外设:2个ADC(10通道)、3个通用定时器、1个高级定时器、I2C、USART等
5.2 外围电路设计
-
按键模块:6个轻触开关,一端接GND,另一端接GPIO并配置内部上拉电阻。
-
LED指示:4个Φ5mm LED,串联220Ω电阻限流,高电平点亮。
-
OLED显示:0.96寸,SSD1306驱动,I2C接口(仅需两根信号线)。
-
MQ2烟雾传感器:模拟电压输出接ADC输入,预热3分钟以上得到稳定基线。
-
DS18B20温度传感器:寄生电源模式,数据线需接4.7k上拉电阻。
-
电机及驱动器:使用L298N或TB6612,输入PWM信号和方向信号(本系统不需换向)。
5.3 电源设计
-
系统供电:5V USB或12V适配器
-
降压至3.3V给STM32、OLED、DS18B20
-
电机驱动单独供电(避免干扰)

六、传感器介绍
6.1 MQ-2烟雾/可燃气体传感器
-
检测气体:液化气、丙烷、氢气、甲烷、烟雾等
-
输出电压:0.1V~5V(浓度越高电压越高)
-
灵敏度:可通过电位器调节阈值
-
使用注意:初次上电需预热60秒,避免接触有机溶剂
6.2 DS18B20数字温度传感器
-
分辨率:9~12位可配置(默认12位)
-
测温范围:-55℃ ~ +125℃
-
精度:±0.5℃(-10℃~+85℃)
-
通信协议:单总线(1-Wire),每个传感器有唯一64位ROM ID
七、软件架构
7.1 程序模块划分
├──main.c // 主循环、状态机
├── bsp_key.c/h // 按键扫描与消抖
├── bsp_led.c/h // LED控制
├── bsp_pwm.c/h // PWM初始化与占空比设置
├── bsp_adc.c/h // ADC初始化及读取MQ2
├── ds18b20.c/h // DS18B20操作函数
├── oled.c/h // OLED显示驱动
├── timer.c/h // 系统定时器(1ms基准)
└── control.c/h // 火力控制、倒计时管理
7.2 状态机设计
系统主状态:灶台开关状态
void STATE_ONOFF(u8 key)
{
if(key == 4)
{
u8 old_onoff = onoff;
onoff++;
if(onoff >= 2) onoff = 0;
// 设备从开变为关时,取消倒计时
if(old_onoff == 1 && onoff == 0)
{
timer_state = 0;
timer_100ms_cnt = 0;
}
}
if(onoff == 0)
OLED_ShowCHinese(48, 2, 16);
if(onoff == 1)
OLED_ShowCHinese(48, 2, 15);
OLED_ShowString(80, 4, " ", 16);
}
八、关键代码展示
8.1 主循环逻辑
(main.c)
int main(void)
{
static u8 test_cnt = 0;
int key_value; //按键值
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
Adc1_Channe_Init(); //ADC通道初始化
KEY_Init(); //按键引脚初始化
LED_Init(); //蜂鸣器初始化
OLED_Init(); //OLED初始化
OLED_Clear(); //OLED清屏
STMFLASH_Read(FLASH_SAVE_ADDR + 10, &A_DS18B20_Temp, 2); //开机读取温度阈值
STMFLASH_Read(FLASH_SAVE_ADDR + 12, &M_DS18B20_Temp, 2); //开机读取温度阈值
STMFLASH_Read(FLASH_SAVE_ADDR + 14, &A_mq2_value, 2); //开机读取湿度阈值
while( DS18B20_Init()) //判断DS18B20是否初始化成功
{
OLED_ShowString(0, 0, "DS18B20 Error", 16);
}
while(1)
{
key_value = KEY_Scan(0); //按键数据
DS18B20_Temp = DS18B20_Get_Temp() / 10;
DisPlay_Temp(32, 0, 16); //显示温度
smoke_value = Get_SmokeValue_Filtered(); // 精确值
smoke_percent = (u8)smoke_value; // 取整用于显示整数
// 温度保护
if(DS18B20_Temp > 30 ||smoke_value>35)
{
if(onoff == 1 || timer_state != 0)
{
onoff = 0;
timer_state = 0;
timer_100ms_cnt = 0;
}
}
//速度选择
if(fire_level == 0) sd_value = 19200;
if(fire_level == 1) sd_value = 24000;
if(fire_level == 2) sd_value = 28800;
OLED_ShowCHinese(64, 0, 2);
OLED_ShowCHinese(80, 0, 3);
sprintf((char*)buff, ":%2d", smoke_value); // 例:":25.6"
OLED_ShowString(96, 0, buff, 16); // 原位置显示百分比
//第二行显示:开关
OLED_ShowCHinese(0, 2, 13);
OLED_ShowCHinese(16, 2, 14);
OLED_ShowString(32, 2, ":", 16);
STATE_ONOFF(key_value);
//第三行显示:等级
OLED_ShowCHinese(0, 4, 6);
OLED_ShowCHinese(16, 4, 7);
OLED_ShowCHinese(32, 4, 8);
OLED_ShowCHinese(48, 4, 9);
OLED_ShowString(64, 4, ":", 16);
//第四行显示:时间
OLED_ShowCHinese(0, 6, 4);
OLED_ShowCHinese(16, 6, 5);
//控制执行
if(onoff == 1)
{
SPEED_UP(key_value);
SPEED_DOWN(key_value);
MOTOR_Init_COLD( sd_value, sd_value );
LED = 0;
if(fire_level == 0)
{
LED1 = 0;
}
else
{
LED1 = 1;
}
if(fire_level == 1)
{
LED2 = 0;
}
else
{
LED2 = 1;
}
if(fire_level == 2)
{
LED3 = 0;
}
else
{
LED3 = 1;
}
}
else
{
MOTOR_Init_COLD( 0, 0 );
LED = 1;
LED1 = 1;
LED2 = 1;
LED3 = 1;
}
// ========== 倒计时处理 ==========
// 100ms 定时(利用 delay_ms(100) 保证每次循环约 100ms)
timer_100ms_cnt++;
if(timer_100ms_cnt >= 10) // 1秒到
{
timer_100ms_cnt = 0;
if(timer_state == 2 && timer_remain > 0)
{
timer_remain--;
if(timer_remain == 0)
{
timer_state = 0; // 倒计时结束,回到空闲
onoff = 0;
// BEEP_ON(); delay_ms(200); BEEP_OFF();
}
}
}
// 按键处理(优先级:先处理倒计时按键)
Handle_Timer(key_value);
Display_Timer(); // 刷新第6行倒计时显示
delay_ms(100);
}
}
8.2 DS18B20读取温度
(单总线时序)
#include "ds18b20.h"
//复位DS18B20
u8 Temp[15];
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PG11 OUTPUT
DS18B20_DQ_OUT = 0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT = 1; //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry = 0;
DS18B20_IO_IN(); //SET PG11 INPUT
while (DS18B20_DQ_IN && retry < 200)
{
retry++;
delay_us(1);
};
if(retry >= 200)return 1;
else retry = 0;
while (!DS18B20_DQ_IN && retry < 240)
{
retry++;
delay_us(1);
};
if(retry >= 240)return 1;
return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_IO_OUT(); //SET PG11 OUTPUT
DS18B20_DQ_OUT = 0;
delay_us(2);
DS18B20_DQ_OUT = 1;
DS18B20_IO_IN(); //SET PG11 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data = 1;
else data = 0;
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)
{
u8 i, j, dat;
dat = 0;
for (i = 1; i <= 8; i++)
{
j = DS18B20_Read_Bit();
dat = (j << 7) | (dat >> 1);
}
return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT(); //SET PG11 OUTPUT;
for (j = 1; j <= 8; j++)
{
testb = dat & 0x01;
dat = dat >> 1;
if (testb)
{
DS18B20_DQ_OUT = 0; // Write 1
delay_us(2);
DS18B20_DQ_OUT = 1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT = 0; // Write 0
delay_us(60);
DS18B20_DQ_OUT = 1;
delay_us(2);
}
}
}
//开始温度转换
void DS18B20_Start(void)
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0x44); // convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PORTG口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PORTG.11 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_0); //输出1
DS18B20_Rst();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL, TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
TL = DS18B20_Read_Byte(); // LSB
TH = DS18B20_Read_Byte(); // MSB
if(TH > 7)
{
TH = ~TH;
TL = ~TL;
temp = 0; //温度为负
} else temp = 1; //温度为正
tem = TH; //获得高八位
tem <<= 8;
tem += TL; //获得底八位
tem = (float)tem * 0.625; //转换
if(temp)return tem; //返回温度值
else return -tem;
}
void DisPlay_Temp(u16 x, u16 y, u8 size)
{
short temperature; //保存温度
temperature = DS18B20_Get_Temp();
// printf("%d\r\n",temperature);
//带小数部分
// if(temperature<0)
// {
// temperature=-temperature; //转为正数
// sprintf((char*)Temp,"Temp:-%d.%dC",temperature/10,temperature%10);
// }else sprintf((char*)Temp,"Temp:+%d.%dC",temperature/10,temperature%10);
//
//无小数部分
// if(temperature<0)
// {
// temperature=-temperature; //转为正数
// sprintf((char*)Temp,"Temp:-%dC",temperature/10);
// }else sprintf((char*)Temp,"Temp:+%dC",temperature/10);
//
if(temperature < 0)
{
temperature = -temperature; //转为正数
sprintf((char*)Temp, ":-%dC", temperature / 10);
} else sprintf((char*)Temp, ":%dC", temperature / 10);
OLED_ShowCHinese(0, 0, 0);
OLED_ShowCHinese(16, 0, 1);
OLED_ShowString(x, y, Temp, size); //显示温度
}
九、总结与展望
本文完成了一款基于STM32F103C8T6的智能灶台控制系统,实现了按键交互、倒计时定时、多档火力调节、烟雾/温度安全监测、电机模拟执行等功能。系统运行稳定,响应及时,验证了设计方案的可行性。
觉得有用的话,点个关注不迷路,持续分享毕设干货
> 源码下载:如需完整Keil工程(包含所有驱动及注释),请关注、评论、并私信作者获取网盘链接。