基于STM32的智能灶台控制系统设计与实现

一、系统方案介绍

随着物联网技术的快速发展,传统厨房设备正向智能化、安全化方向升级。本文设计了一款基于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 实现流程

  1. 初始化系统:时钟配置(72MHz)、GPIO、ADC、定时器PWM、I2C、单总线。

  2. 显示界面:OLED显示初始状态(火力关闭,烟雾/温度数值,倒计时00:00)。

  3. 按键扫描:循环检测6个按键动作,执行对应功能(带软件消抖)。

  4. 传感器采集:每200ms读取一次烟雾ADC值和DS18B20温度值。

  5. 安全判断:若烟雾值 > 阈值 或 温度 > 阈值 → 关闭灶台(`stove_on = 0`,电机停转,LED1灭)。

  6. 倒计时处理:倒计时模式下,每秒递减,减至0时自动关闭灶台。

  7. 火力输出:根据当前火力等级(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工程(包含所有驱动及注释),请关注、评论、并私信作者获取网盘链接。

相关推荐
iCxhust2 小时前
8088单板机DIY---机体激活,聆听另一个世界的声音(三)
单片机·嵌入式硬件·微机原理·8088单板机
csdn_aspnet2 小时前
单片机IO不够?ULN2003A救急方案,结合STM32/ESP32实战案例,讲透达林顿阵列在IoT硬件中的高效复用
stm32·单片机·物联网·esp32·iot·uln2003a
时空自由民.11 小时前
STM32配置Timer+DMA读取ADC数据
stm32·单片机·嵌入式硬件
华普微HOPERF12 小时前
数字隔离器,如何确保MCU不受储能系统中的高电压、大电流影响?
单片机·嵌入式硬件
小麦嵌入式13 小时前
FPGA入门(四):时序逻辑计数器原理与 LED 闪烁实现
linux·驱动开发·stm32·嵌入式硬件·fpga开发·硬件工程·dsp开发
搁浅小泽13 小时前
常用电子元器件
单片机·嵌入式硬件·可靠性工程师
zhaoshuzhaoshu14 小时前
嵌入式开发之时钟树解析-SMT32平台
嵌入式硬件
三佛科技-1873661339714 小时前
FT60E211-RB省成本,提效率!IO型8位单片机智能家居产品应用解析
单片机·嵌入式硬件·智能家居
哄娃睡觉15 小时前
STM32F407VET6 的串口分别对应了哪些引脚?
stm32