前言
去年帮朋友的小家电公司做了一款智能电饭锅的控制板开发,从最初只会把饭煮熟,到后来能做出柴火饭、煲仔饭、蒸蛋糕,中间研究了不少烹饪工艺和控制算法。
电饭锅看起来简单,实际上要把饭煮好吃,背后的温度控制逻辑相当讲究。今天把整个控制系统的设计思路分享出来,包括多模式切换、温度曲线设计、状态机实现等,希望对做类似项目的朋友有帮助。
电饭锅工作原理
基本结构
┌─────────────────────────────────────────────────────────────────┐
│ 智能电饭锅结构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ 上盖 │ ← 蒸汽阀、上盖温度传感器 │
│ │ ┌─────┐ │ │
│ │ │蒸汽阀│ │ │
│ │ └─────┘ │ │
│ └─────┬─────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ │ 内胆 │ ← 食物容器 │
│ │ ┌─────────┐ │ │
│ │ │ 米+水 │ │ │
│ │ └─────────┘ │ │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ │
│ ┌─────────────────────┴─────────────────────┐ │
│ │ 发热盘 │ │
│ │ ┌─────┐ ┌─────────────────┐ ┌─────┐ │ │
│ │ │NTC1 │ │ 加热线圈 │ │NTC2 │ │ │
│ │ │底部 │ │ (IH/电热) │ │侧面 │ │ │
│ │ └─────┘ └─────────────────┘ └─────┘ │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 控制板 │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ │ MCU │ │ 继电器│ │ IGBT │ │ 显示 │ │ 按键 │ │ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
加热方式
1. 传统电热盘
- 电阻丝发热,传导到内胆底部
- 功率固定,通过通断时间控制加热量
- 成本低,但加热不均匀
2. IH电磁加热
- 高频交流电产生交变磁场
- 内胆自身发热(涡流效应)
- 加热均匀,可精确控制功率
- 成本高,效率高
c
/**
* 加热方式定义
*/
typedef enum {
HEATER_TYPE_RESISTANCE, // 电阻加热
HEATER_TYPE_IH // IH电磁加热
} heater_type_t;
/**
* IH加热功率控制
* 通过PWM占空比控制IGBT开关频率
*/
typedef struct {
uint16_t frequency; // 工作频率 (20-50kHz)
uint8_t duty_cycle; // 占空比 (0-100%)
uint16_t power_watt; // 对应功率 (W)
} ih_power_config_t;
// IH功率档位表(示例)
static const ih_power_config_t ih_power_table[] = {
{25000, 10, 100}, // 100W 保温
{25000, 20, 200}, // 200W 小火
{25000, 40, 400}, // 400W 中火
{25000, 60, 600}, // 600W 中大火
{25000, 80, 800}, // 800W 大火
{25000, 100, 1000}, // 1000W 最大功率
};
温度传感器
c
/**
* NTC热敏电阻温度测量
*
* NTC特性: R = R25 × exp(B × (1/T - 1/298.15))
* 其中:
* R25: 25°C时的阻值(通常10kΩ或100kΩ)
* B: B值常数(通常3950或3435)
* T: 开尔文温度
*/
typedef struct {
float r25; // 25°C标称阻值 (Ω)
float b_value; // B值
float r_series; // 串联分压电阻 (Ω)
float v_ref; // 参考电压 (V)
} ntc_config_t;
/**
* 从ADC值计算温度
*/
float ntc_calc_temperature(ntc_config_t *config, uint16_t adc_value, uint16_t adc_max)
{
// 计算NTC电阻值
// Vntc = Vref × ADC / ADC_MAX
// Vntc = Vref × Rntc / (Rntc + Rs)
// 解得: Rntc = Rs × ADC / (ADC_MAX - ADC)
if (adc_value >= adc_max - 1) {
return -40.0f; // 开路,返回最低温度
}
if (adc_value <= 1) {
return 200.0f; // 短路,返回最高温度
}
float r_ntc = config->r_series * adc_value / (adc_max - adc_value);
// 计算温度 (开尔文)
// 1/T = 1/298.15 + ln(R/R25)/B
float temp_k = 1.0f / (1.0f/298.15f + logf(r_ntc/config->r25) / config->b_value);
// 转换为摄氏度
return temp_k - 273.15f;
}
/**
* 温度传感器配置
*/
typedef struct {
ntc_config_t bottom_ntc; // 底部温度传感器
ntc_config_t side_ntc; // 侧面温度传感器
ntc_config_t lid_ntc; // 上盖温度传感器
} temp_sensors_t;
// 典型配置
static temp_sensors_t g_temp_sensors = {
.bottom_ntc = {.r25 = 100000, .b_value = 3950, .r_series = 10000, .v_ref = 3.3f},
.side_ntc = {.r25 = 100000, .b_value = 3950, .r_series = 10000, .v_ref = 3.3f},
.lid_ntc = {.r25 = 100000, .b_value = 3950, .r_series = 10000, .v_ref = 3.3f}
};
烹饪模式设计
模式分类
c
/**
* 烹饪模式定义
*/
typedef enum {
// 基础模式
MODE_STANDARD_RICE, // 标准煮饭
MODE_QUICK_RICE, // 快速煮饭
MODE_PORRIDGE, // 煮粥
// 米饭进阶模式
MODE_FIREWOOD_RICE, // 柴火饭(锅巴饭)
MODE_CLAYPOT_RICE, // 煲仔饭
MODE_MIXED_RICE, // 杂粮饭
MODE_SUSHI_RICE, // 寿司饭
MODE_BROWN_RICE, // 糙米饭
// 特殊功能
MODE_STEAM, // 蒸煮
MODE_CAKE, // 蛋糕
MODE_SOUP, // 煲汤
MODE_YOGURT, // 酸奶(发酵)
MODE_SLOW_COOK, // 慢炖
// 辅助功能
MODE_KEEP_WARM, // 保温
MODE_REHEAT, // 再加热
MODE_COUNT
} cooking_mode_t;
/**
* 模式显示名称
*/
static const char* mode_names[] = {
[MODE_STANDARD_RICE] = "精华煮",
[MODE_QUICK_RICE] = "快速煮",
[MODE_PORRIDGE] = "煮粥",
[MODE_FIREWOOD_RICE] = "柴火饭",
[MODE_CLAYPOT_RICE] = "煲仔饭",
[MODE_MIXED_RICE] = "杂粮饭",
[MODE_SUSHI_RICE] = "寿司饭",
[MODE_BROWN_RICE] = "糙米饭",
[MODE_STEAM] = "蒸煮",
[MODE_CAKE] = "蛋糕",
[MODE_SOUP] = "煲汤",
[MODE_YOGURT] = "酸奶",
[MODE_SLOW_COOK] = "慢炖",
[MODE_KEEP_WARM] = "保温",
[MODE_REHEAT] = "再加热",
};
烹饪阶段定义
每种模式都由多个阶段组成,每个阶段有不同的温度目标和持续时间:
c
/**
* 烹饪阶段
*/
typedef enum {
STAGE_IDLE, // 空闲
STAGE_PREHEAT, // 预热
STAGE_SOAK, // 浸泡(吸水)
STAGE_HEAT_UP, // 升温
STAGE_BOIL, // 沸腾
STAGE_SIMMER, // 焖煮
STAGE_CRUST, // 结锅巴
STAGE_BRAISE, // 焖饭
STAGE_COOL_DOWN, // 降温
STAGE_KEEP_WARM, // 保温
STAGE_COMPLETE // 完成
} cooking_stage_t;
/**
* 阶段参数
*/
typedef struct {
cooking_stage_t stage; // 阶段类型
float target_temp; // 目标温度 (°C)
float temp_tolerance; // 温度容差 (°C)
uint16_t duration_sec; // 持续时间 (秒),0表示按条件退出
uint8_t power_level; // 功率档位 (0-100%)
uint8_t exit_on_temp; // 是否达到温度就退出
uint8_t exit_on_time; // 是否时间到就退出
} stage_config_t;
/**
* 完整烹饪配方
*/
typedef struct {
cooking_mode_t mode;
const char *name;
uint8_t num_stages;
stage_config_t stages[12]; // 最多12个阶段
uint16_t total_time_min; // 预计总时间(分钟)
} recipe_t;
各模式温度曲线详解
标准煮饭模式
这是最基础的模式,温度曲线经过精心设计:
温度(°C)
↑
120 │ ┌──────┐
│ ╱│沸腾 │╲
105 │ ╱ │ │ ╲
│ ╱ └──────┘ ╲ 焖饭
100 │────────────────╱ ╲──────────
│ 升温 ╱ ╲
70 │ ┌────────╱ ╲
│ ╱│ 吸水 │ ╲
50 │ ╱ │ │ ╲
│╱ └───────┘ ╲ 保温
40 │预热 ────
└─────┬──────┬─────┬──────┬─────┬──────┬───→ 时间
5min 15min 8min 15min 10min 保温
阶段说明:
1. 预热(5min): 40→50°C,让米粒表面湿润
2. 吸水(15min): 50→70°C,米粒充分吸水膨胀
3. 升温(8min): 70→100°C,快速升温
4. 沸腾(15min): 100-105°C,大火煮透
5. 焖饭(10min): 关火焖,让水分均匀分布
6. 保温: 维持60-70°C
c
/**
* 标准煮饭配方
*/
static const recipe_t recipe_standard_rice = {
.mode = MODE_STANDARD_RICE,
.name = "精华煮",
.num_stages = 6,
.stages = {
// 阶段1: 预热
{
.stage = STAGE_PREHEAT,
.target_temp = 50.0f,
.temp_tolerance = 3.0f,
.duration_sec = 300, // 5分钟
.power_level = 30,
.exit_on_temp = 1,
.exit_on_time = 1
},
// 阶段2: 吸水
{
.stage = STAGE_SOAK,
.target_temp = 65.0f,
.temp_tolerance = 5.0f,
.duration_sec = 900, // 15分钟
.power_level = 20,
.exit_on_temp = 0,
.exit_on_time = 1
},
// 阶段3: 升温
{
.stage = STAGE_HEAT_UP,
.target_temp = 100.0f,
.temp_tolerance = 2.0f,
.duration_sec = 600, // 最长10分钟
.power_level = 100,
.exit_on_temp = 1,
.exit_on_time = 0
},
// 阶段4: 沸腾
{
.stage = STAGE_BOIL,
.target_temp = 103.0f,
.temp_tolerance = 3.0f,
.duration_sec = 900, // 15分钟
.power_level = 80,
.exit_on_temp = 0,
.exit_on_time = 1
},
// 阶段5: 焖饭
{
.stage = STAGE_BRAISE,
.target_temp = 95.0f,
.temp_tolerance = 5.0f,
.duration_sec = 600, // 10分钟
.power_level = 10,
.exit_on_temp = 0,
.exit_on_time = 1
},
// 阶段6: 保温
{
.stage = STAGE_KEEP_WARM,
.target_temp = 65.0f,
.temp_tolerance = 5.0f,
.duration_sec = 0, // 持续保温
.power_level = 5,
.exit_on_temp = 0,
.exit_on_time = 0
}
},
.total_time_min = 45
};
柴火饭模式
柴火饭的特点是底部有脆香的锅巴,需要在最后阶段加大火力:
温度(°C)
↑
130 │ ┌────┐
│ ╱│锅巴│
120 │ ┌───────╱ │阶段│
│ ╱│沸腾 │ └────┘
105 │ ╱ │ │
100 │─────────────────╱ └──────┘
│ ╱
70 │ ┌─────────╱
│ ╱│ 吸水 │
50 │ ╱ └────────┘
│╱
└──────┬───────┬─────┬──────┬─────┬────→ 时间
5min 12min 8min 12min 8min
特殊处理:
- 吸水时间略短(米芯保留些硬度)
- 沸腾后增加"锅巴阶段"
- 锅巴阶段底部温度达到120-130°C
- 全程火力更大
c
/**
* 柴火饭配方
*/
static const recipe_t recipe_firewood_rice = {
.mode = MODE_FIREWOOD_RICE,
.name = "柴火饭",
.num_stages = 7,
.stages = {
// 预热
{
.stage = STAGE_PREHEAT,
.target_temp = 55.0f,
.temp_tolerance = 3.0f,
.duration_sec = 300,
.power_level = 40,
.exit_on_temp = 1,
.exit_on_time = 1
},
// 吸水(时间较短)
{
.stage = STAGE_SOAK,
.target_temp = 68.0f,
.temp_tolerance = 5.0f,
.duration_sec = 720, // 12分钟
.power_level = 25,
.exit_on_temp = 0,
.exit_on_time = 1
},
// 快速升温
{
.stage = STAGE_HEAT_UP,
.target_temp = 100.0f,
.temp_tolerance = 2.0f,
.duration_sec = 480,
.power_level = 100,
.exit_on_temp = 1,
.exit_on_time = 0
},
// 大火沸腾
{
.stage = STAGE_BOIL,
.target_temp = 105.0f,
.temp_tolerance = 3.0f,
.duration_sec = 720, // 12分钟
.power_level = 90,
.exit_on_temp = 0,
.exit_on_time = 1
},
// ★ 锅巴阶段(关键)
{
.stage = STAGE_CRUST,
.target_temp = 125.0f, // 底部高温形成锅巴
.temp_tolerance = 5.0f,
.duration_sec = 480, // 8分钟
.power_level = 70, // 中高火
.exit_on_temp = 0,
.exit_on_time = 1
},
// 焖饭
{
.stage = STAGE_BRAISE,
.target_temp = 90.0f,
.temp_tolerance = 5.0f,
.duration_sec = 300,
.power_level = 0, // 关火焖
.exit_on_temp = 0,
.exit_on_time = 1
},
// 保温
{
.stage = STAGE_KEEP_WARM,
.target_temp = 65.0f,
.temp_tolerance = 5.0f,
.duration_sec = 0,
.power_level = 5,
.exit_on_temp = 0,
.exit_on_time = 0
}
},
.total_time_min = 42
};
煲仔饭模式
煲仔饭需要先煮饭,然后加入腊肠等配料继续加热:
c
/**
* 煲仔饭配方
* 特点:底部锅巴+中途可加料+酱汁焦香
*/
static const recipe_t recipe_claypot_rice = {
.mode = MODE_CLAYPOT_RICE,
.name = "煲仔饭",
.num_stages = 8,
.stages = {
// 预热
{STAGE_PREHEAT, 50.0f, 3.0f, 240, 35, 1, 1},
// 吸水
{STAGE_SOAK, 65.0f, 5.0f, 600, 20, 0, 1},
// 升温
{STAGE_HEAT_UP, 100.0f, 2.0f, 480, 100, 1, 0},
// 初次沸腾(此时可以提示加料)
{STAGE_BOIL, 103.0f, 3.0f, 600, 80, 0, 1},
// 加料后继续加热
{STAGE_SIMMER, 100.0f, 3.0f, 480, 60, 0, 1},
// 锅巴+焦香(大火收汁)
{STAGE_CRUST, 130.0f, 5.0f, 420, 85, 0, 1},
// 焖
{STAGE_BRAISE, 85.0f, 5.0f, 300, 0, 0, 1},
// 保温
{STAGE_KEEP_WARM, 65.0f, 5.0f, 0, 5, 0, 0}
},
.total_time_min = 48
};
蒸蛋糕模式
蛋糕需要恒温蒸制,温度控制非常关键:
温度(°C)
↑
110 │ ┌─────────────────────────────┐
│ ╱│ 恒温蒸制 │
100 │──────╱ │ (100-105°C) │
│ ╱ └─────────────────────────────┘
80 │ ╱
│ ╱ 升温
60 │ ╱
│ ╱
40 │╱ 预热
└──────┬─────────────────────────────────→ 时间
5min 50-60min
关键点:
- 升温要平稳,避免表面过早结皮
- 蒸制温度恒定在100-105°C
- 时间根据蛋糕大小调整
- 不能沸腾过于剧烈(会导致蛋糕开裂)
c
/**
* 蛋糕配方
*/
static const recipe_t recipe_cake = {
.mode = MODE_CAKE,
.name = "蛋糕",
.num_stages = 4,
.stages = {
// 预热
{
.stage = STAGE_PREHEAT,
.target_temp = 60.0f,
.temp_tolerance = 3.0f,
.duration_sec = 180,
.power_level = 40,
.exit_on_temp = 1,
.exit_on_time = 1
},
// 缓慢升温
{
.stage = STAGE_HEAT_UP,
.target_temp = 100.0f,
.temp_tolerance = 2.0f,
.duration_sec = 300, // 5分钟缓升温
.power_level = 50, // 中等功率,避免过快
.exit_on_temp = 1,
.exit_on_time = 0
},
// 恒温蒸制(核心阶段)
{
.stage = STAGE_STEAM,
.target_temp = 102.0f, // 略高于100°C
.temp_tolerance = 2.0f, // 严格控温
.duration_sec = 3000, // 50分钟
.power_level = 35, // 维持蒸汽,不要太猛
.exit_on_temp = 0,
.exit_on_time = 1
},
// 完成(不保温,需要取出)
{
.stage = STAGE_COMPLETE,
.target_temp = 0.0f,
.temp_tolerance = 0.0f,
.duration_sec = 0,
.power_level = 0,
.exit_on_temp = 0,
.exit_on_time = 0
}
},
.total_time_min = 60
};
煮粥模式
粥需要长时间小火慢煮,防止溢出是关键:
c
/**
* 煮粥配方
* 特点:时间长、火力小、防溢出
*/
static const recipe_t recipe_porridge = {
.mode = MODE_PORRIDGE,
.name = "煮粥",
.num_stages = 5,
.stages = {
// 预热
{STAGE_PREHEAT, 50.0f, 3.0f, 300, 30, 1, 1},
// 升温
{STAGE_HEAT_UP, 95.0f, 2.0f, 600, 80, 1, 0},
// 沸腾(短暂,防溢)
{STAGE_BOIL, 98.0f, 2.0f, 300, 50, 0, 1},
// 小火慢煮(核心)
{
.stage = STAGE_SIMMER,
.target_temp = 93.0f, // 低于沸点,防溢
.temp_tolerance = 3.0f,
.duration_sec = 3600, // 60分钟慢煮
.power_level = 25, // 小火
.exit_on_temp = 0,
.exit_on_time = 1
},
// 保温
{STAGE_KEEP_WARM, 65.0f, 5.0f, 0, 5, 0, 0}
},
.total_time_min = 75
};
酸奶模式(发酵)
酸奶需要长时间恒温发酵:
c
/**
* 酸奶配方
* 特点:低温恒温、时间长
*/
static const recipe_t recipe_yogurt = {
.mode = MODE_YOGURT,
.name = "酸奶",
.num_stages = 3,
.stages = {
// 加热到发酵温度
{
.stage = STAGE_HEAT_UP,
.target_temp = 42.0f, // 发酵最佳温度
.temp_tolerance = 1.0f, // 精确控温
.duration_sec = 600,
.power_level = 20,
.exit_on_temp = 1,
.exit_on_time = 0
},
// 恒温发酵
{
.stage = STAGE_FERMENT,
.target_temp = 42.0f,
.temp_tolerance = 2.0f,
.duration_sec = 28800, // 8小时
.power_level = 5, // 极小功率维持温度
.exit_on_temp = 0,
.exit_on_time = 1
},
// 完成
{STAGE_COMPLETE, 0.0f, 0.0f, 0, 0, 0, 0}
},
.total_time_min = 490 // 约8小时
};
状态机设计
主状态机
c
/**
* 系统主状态
*/
typedef enum {
STATE_IDLE, // 空闲,等待用户操作
STATE_STANDBY, // 待机,已选择模式
STATE_COOKING, // 烹饪中
STATE_PAUSED, // 暂停
STATE_KEEP_WARM, // 保温中
STATE_COMPLETE, // 完成
STATE_ERROR // 错误
} system_state_t;
/**
* 系统事件
*/
typedef enum {
EVENT_NONE,
EVENT_START, // 开始按键
EVENT_CANCEL, // 取消按键
EVENT_MODE_SELECT, // 模式选择
EVENT_TIMER_SET, // 预约设置
EVENT_LID_OPEN, // 开盖
EVENT_LID_CLOSE, // 合盖
EVENT_STAGE_COMPLETE, // 阶段完成
EVENT_TEMP_ALARM, // 温度异常
EVENT_TIMEOUT, // 超时
EVENT_POWER_FAIL // 断电
} system_event_t;
/**
* 系统上下文
*/
typedef struct {
system_state_t state;
cooking_mode_t mode;
uint8_t current_stage;
uint32_t stage_start_time;
uint32_t cook_start_time;
// 温度
float temp_bottom;
float temp_side;
float temp_lid;
// 目标
float target_temp;
uint8_t target_power;
// 预约
uint8_t timer_enabled;
uint32_t timer_target;
// 错误
uint8_t error_code;
} cooker_context_t;
static cooker_context_t g_ctx;
状态转换
c
/**
* 状态机转换表
*/
typedef struct {
system_state_t current;
system_event_t event;
system_state_t next;
void (*action)(void);
} state_transition_t;
// 前向声明动作函数
static void action_start_cooking(void);
static void action_stop_cooking(void);
static void action_pause(void);
static void action_resume(void);
static void action_enter_keep_warm(void);
static void action_handle_error(void);
static const state_transition_t transitions[] = {
// 空闲状态
{STATE_IDLE, EVENT_MODE_SELECT, STATE_STANDBY, NULL},
// 待机状态
{STATE_STANDBY, EVENT_START, STATE_COOKING, action_start_cooking},
{STATE_STANDBY, EVENT_CANCEL, STATE_IDLE, NULL},
{STATE_STANDBY, EVENT_MODE_SELECT, STATE_STANDBY, NULL},
// 烹饪状态
{STATE_COOKING, EVENT_CANCEL, STATE_IDLE, action_stop_cooking},
{STATE_COOKING, EVENT_LID_OPEN, STATE_PAUSED, action_pause},
{STATE_COOKING, EVENT_STAGE_COMPLETE,STATE_COOKING, NULL}, // 内部处理
{STATE_COOKING, EVENT_TEMP_ALARM, STATE_ERROR, action_handle_error},
// 暂停状态
{STATE_PAUSED, EVENT_LID_CLOSE, STATE_COOKING, action_resume},
{STATE_PAUSED, EVENT_CANCEL, STATE_IDLE, action_stop_cooking},
{STATE_PAUSED, EVENT_TIMEOUT, STATE_ERROR, action_handle_error},
// 保温状态
{STATE_KEEP_WARM, EVENT_CANCEL, STATE_IDLE, action_stop_cooking},
{STATE_KEEP_WARM, EVENT_START, STATE_COOKING, action_start_cooking},
// 完成状态
{STATE_COMPLETE, EVENT_CANCEL, STATE_IDLE, NULL},
{STATE_COMPLETE, EVENT_START, STATE_KEEP_WARM, action_enter_keep_warm},
// 错误状态
{STATE_ERROR, EVENT_CANCEL, STATE_IDLE, NULL},
};
#define NUM_TRANSITIONS (sizeof(transitions) / sizeof(transitions[0]))
/**
* 状态机处理
*/
void state_machine_process(system_event_t event)
{
for (int i = 0; i < NUM_TRANSITIONS; i++) {
if (transitions[i].current == g_ctx.state &&
transitions[i].event == event) {
// 执行动作
if (transitions[i].action) {
transitions[i].action();
}
// 状态转换
system_state_t old_state = g_ctx.state;
g_ctx.state = transitions[i].next;
printf("[FSM] %d -> %d (event: %d)\n", old_state, g_ctx.state, event);
// 更新显示
display_update_state(g_ctx.state);
return;
}
}
// 未找到匹配的转换
printf("[FSM] No transition for state=%d, event=%d\n", g_ctx.state, event);
}
烹饪阶段控制
c
/**
* 配方管理
*/
static const recipe_t* recipes[] = {
&recipe_standard_rice,
&recipe_firewood_rice,
&recipe_claypot_rice,
&recipe_porridge,
&recipe_cake,
&recipe_yogurt,
// ... 其他配方
};
static const recipe_t* g_current_recipe = NULL;
/**
* 开始烹饪
*/
static void action_start_cooking(void)
{
// 获取当前模式的配方
g_current_recipe = recipes[g_ctx.mode];
if (g_current_recipe == NULL) {
g_ctx.error_code = ERR_INVALID_MODE;
g_ctx.state = STATE_ERROR;
return;
}
// 初始化烹饪参数
g_ctx.current_stage = 0;
g_ctx.cook_start_time = get_tick_ms();
g_ctx.stage_start_time = g_ctx.cook_start_time;
// 应用第一阶段参数
apply_stage_config(&g_current_recipe->stages[0]);
printf("[COOK] Started: %s\n", g_current_recipe->name);
printf("[COOK] Stage 0: %d, target=%.1f°C, duration=%ds\n",
g_current_recipe->stages[0].stage,
g_current_recipe->stages[0].target_temp,
g_current_recipe->stages[0].duration_sec);
// 开启加热
heater_enable(1);
// 更新显示
display_show_cooking(g_current_recipe->name,
g_current_recipe->total_time_min);
}
/**
* 应用阶段配置
*/
static void apply_stage_config(const stage_config_t *stage)
{
g_ctx.target_temp = stage->target_temp;
g_ctx.target_power = stage->power_level;
// 更新PID目标
pid_set_target(stage->target_temp);
}
/**
* 检查阶段是否完成
*/
static int check_stage_complete(const stage_config_t *stage)
{
uint32_t elapsed = get_tick_ms() - g_ctx.stage_start_time;
uint32_t duration_ms = stage->duration_sec * 1000;
// 时间条件
if (stage->exit_on_time && elapsed >= duration_ms) {
return 1;
}
// 温度条件
if (stage->exit_on_temp) {
float temp_error = g_ctx.temp_bottom - stage->target_temp;
if (fabsf(temp_error) <= stage->temp_tolerance) {
return 1;
}
}
return 0;
}
/**
* 进入下一阶段
*/
static void advance_to_next_stage(void)
{
g_ctx.current_stage++;
if (g_ctx.current_stage >= g_current_recipe->num_stages) {
// 所有阶段完成
cooking_complete();
return;
}
const stage_config_t *next = &g_current_recipe->stages[g_ctx.current_stage];
g_ctx.stage_start_time = get_tick_ms();
apply_stage_config(next);
printf("[COOK] Stage %d: %d, target=%.1f°C, duration=%ds\n",
g_ctx.current_stage,
next->stage,
next->target_temp,
next->duration_sec);
// 特殊阶段处理
switch (next->stage) {
case STAGE_KEEP_WARM:
g_ctx.state = STATE_KEEP_WARM;
buzzer_beep(3); // 提示音
display_show_keep_warm();
break;
case STAGE_COMPLETE:
cooking_complete();
break;
case STAGE_CRUST:
// 锅巴阶段,可以播放特殊提示
display_show_message("正在形成锅巴...");
break;
default:
break;
}
}
/**
* 烹饪完成
*/
static void cooking_complete(void)
{
heater_enable(0);
g_ctx.state = STATE_COMPLETE;
uint32_t total_time = (get_tick_ms() - g_ctx.cook_start_time) / 1000;
printf("[COOK] Complete! Total time: %d:%02d\n",
total_time / 60, total_time % 60);
buzzer_melody_complete();
display_show_complete();
}
/**
* 烹饪主循环(定时器中调用,如每100ms)
*/
void cooking_process(void)
{
if (g_ctx.state != STATE_COOKING && g_ctx.state != STATE_KEEP_WARM) {
return;
}
// 读取温度
g_ctx.temp_bottom = read_temperature(SENSOR_BOTTOM);
g_ctx.temp_side = read_temperature(SENSOR_SIDE);
g_ctx.temp_lid = read_temperature(SENSOR_LID);
// 安全检查
if (g_ctx.temp_bottom > TEMP_MAX_SAFE) {
heater_enable(0);
g_ctx.error_code = ERR_OVER_TEMP;
state_machine_process(EVENT_TEMP_ALARM);
return;
}
// 获取当前阶段
const stage_config_t *stage = &g_current_recipe->stages[g_ctx.current_stage];
// 温度控制
float power = temperature_control(stage);
heater_set_power(power);
// 检查阶段完成
if (check_stage_complete(stage)) {
advance_to_next_stage();
}
// 更新显示
update_cooking_display();
}
温度控制算法
PID控制器
c
/**
* PID控制器
*/
typedef struct {
float kp; // 比例系数
float ki; // 积分系数
float kd; // 微分系数
float target; // 目标温度
float integral; // 积分累积
float prev_error; // 上次误差
float integral_max; // 积分限幅
float output_min; // 输出下限
float output_max; // 输出上限
uint32_t last_time;
} pid_controller_t;
static pid_controller_t g_pid = {
.kp = 8.0f,
.ki = 0.5f,
.kd = 2.0f,
.integral_max = 50.0f,
.output_min = 0.0f,
.output_max = 100.0f
};
/**
* PID计算
*/
float pid_compute(pid_controller_t *pid, float current_temp)
{
uint32_t now = get_tick_ms();
float dt = (now - pid->last_time) / 1000.0f;
pid->last_time = now;
if (dt <= 0 || dt > 1.0f) {
dt = 0.1f; // 默认100ms
}
float error = pid->target - current_temp;
// 积分
pid->integral += error * dt;
// 积分限幅(防止积分饱和)
if (pid->integral > pid->integral_max) {
pid->integral = pid->integral_max;
} else if (pid->integral < -pid->integral_max) {
pid->integral = -pid->integral_max;
}
// 微分
float derivative = (error - pid->prev_error) / dt;
pid->prev_error = error;
// PID输出
float output = pid->kp * error +
pid->ki * pid->integral +
pid->kd * derivative;
// 输出限幅
if (output > pid->output_max) output = pid->output_max;
if (output < pid->output_min) output = pid->output_min;
return output;
}
/**
* 设置PID目标
*/
void pid_set_target(float target)
{
g_pid.target = target;
g_pid.integral = 0; // 目标改变时清除积分
g_pid.prev_error = 0;
}
/**
* 根据阶段调整PID参数
*/
void pid_tune_for_stage(cooking_stage_t stage)
{
switch (stage) {
case STAGE_HEAT_UP:
// 升温阶段:快速响应,允许超调
g_pid.kp = 10.0f;
g_pid.ki = 0.3f;
g_pid.kd = 1.0f;
break;
case STAGE_BOIL:
// 沸腾阶段:维持高温
g_pid.kp = 8.0f;
g_pid.ki = 0.5f;
g_pid.kd = 2.0f;
break;
case STAGE_SIMMER:
case STAGE_KEEP_WARM:
// 保温阶段:平稳控制,避免波动
g_pid.kp = 5.0f;
g_pid.ki = 0.8f;
g_pid.kd = 3.0f;
break;
case STAGE_CRUST:
// 锅巴阶段:需要高温但不能烧焦
g_pid.kp = 6.0f;
g_pid.ki = 0.2f;
g_pid.kd = 4.0f;
break;
default:
// 默认参数
g_pid.kp = 8.0f;
g_pid.ki = 0.5f;
g_pid.kd = 2.0f;
break;
}
}
模糊控制(高级)
对于更精细的温度控制,可以使用模糊控制:
c
/**
* 模糊控制器
* 输入:温度误差(e)、误差变化率(ec)
* 输出:功率调整量
*/
// 模糊集合定义
typedef enum {
NB = 0, // Negative Big
NM, // Negative Medium
NS, // Negative Small
ZO, // Zero
PS, // Positive Small
PM, // Positive Medium
PB // Positive Big
} fuzzy_set_t;
#define FUZZY_LEVELS 7
// 模糊规则表 [误差][误差变化率] -> 输出
// 行:误差(NB~PB),列:误差变化率(NB~PB)
static const int8_t fuzzy_rules[FUZZY_LEVELS][FUZZY_LEVELS] = {
// NB NM NS ZO PS PM PB <- ec
/*NB*/{PB, PB, PM, PM, PS, ZO, ZO},
/*NM*/{PB, PB, PM, PS, PS, ZO, NS},
/*NS*/{PM, PM, PM, PS, ZO, NS, NS},
/*ZO*/{PM, PM, PS, ZO, NS, NM, NM},
/*PS*/{PS, PS, ZO, NS, NS, NM, NM},
/*PM*/{PS, ZO, NS, NM, NM, NM, NB},
/*PB*/{ZO, ZO, NM, NM, NM, NB, NB}
// ↑ e
};
// 隶属度函数参数
static const float fuzzy_centers[] = {-3, -2, -1, 0, 1, 2, 3};
/**
* 计算隶属度(三角形隶属函数)
*/
float calc_membership(float value, float center, float width)
{
float dist = fabsf(value - center);
if (dist >= width) return 0;
return 1.0f - dist / width;
}
/**
* 模糊化
*/
void fuzzify(float value, float scale, float memberships[FUZZY_LEVELS])
{
float normalized = value / scale; // 归一化到[-3, 3]
// 限制范围
if (normalized < -3) normalized = -3;
if (normalized > 3) normalized = 3;
for (int i = 0; i < FUZZY_LEVELS; i++) {
memberships[i] = calc_membership(normalized, fuzzy_centers[i], 1.0f);
}
}
/**
* 模糊推理
*/
float fuzzy_inference(float e_memberships[FUZZY_LEVELS],
float ec_memberships[FUZZY_LEVELS])
{
float output_memberships[FUZZY_LEVELS] = {0};
// 对每条规则
for (int i = 0; i < FUZZY_LEVELS; i++) {
for (int j = 0; j < FUZZY_LEVELS; j++) {
// 取小(AND)
float strength = fminf(e_memberships[i], ec_memberships[j]);
// 输出模糊集合
int output_set = fuzzy_rules[i][j];
// 取大(OR)
if (strength > output_memberships[output_set]) {
output_memberships[output_set] = strength;
}
}
}
// 解模糊(重心法)
float numerator = 0;
float denominator = 0;
for (int i = 0; i < FUZZY_LEVELS; i++) {
numerator += fuzzy_centers[i] * output_memberships[i];
denominator += output_memberships[i];
}
if (denominator < 0.001f) return 0;
return numerator / denominator;
}
/**
* 模糊控制主函数
*/
float fuzzy_control(float target_temp, float current_temp, float *prev_temp)
{
// 计算误差和误差变化率
float e = target_temp - current_temp;
float ec = current_temp - *prev_temp; // 温度变化率(误差变化率的负值)
*prev_temp = current_temp;
// 模糊化
float e_memberships[FUZZY_LEVELS];
float ec_memberships[FUZZY_LEVELS];
fuzzify(e, 20.0f, e_memberships); // 误差范围±60°C
fuzzify(-ec, 5.0f, ec_memberships); // 变化率范围±15°C/周期
// 模糊推理
float output = fuzzy_inference(e_memberships, ec_memberships);
// 输出映射到功率 (0-100%)
float power = 50.0f + output * 16.67f; // [-3,3] -> [0,100]
if (power > 100) power = 100;
if (power < 0) power = 0;
return power;
}
综合温度控制
c
/**
* 温度控制策略
*/
typedef enum {
CTRL_PID, // PID控制
CTRL_FUZZY, // 模糊控制
CTRL_BANG_BANG, // 开关控制(简单模式)
CTRL_HYBRID // 混合控制
} control_strategy_t;
/**
* 综合温度控制
*/
float temperature_control(const stage_config_t *stage)
{
float current_temp = g_ctx.temp_bottom;
float target_temp = stage->target_temp;
float max_power = stage->power_level;
float power = 0;
// 根据阶段选择控制策略
switch (stage->stage) {
case STAGE_HEAT_UP:
// 升温阶段:全功率加热直到接近目标
if (current_temp < target_temp - 10) {
power = max_power;
} else {
// 接近目标时切换到PID
power = pid_compute(&g_pid, current_temp);
}
break;
case STAGE_SOAK:
case STAGE_SIMMER:
// 恒温阶段:PID精确控制
pid_tune_for_stage(stage->stage);
power = pid_compute(&g_pid, current_temp);
break;
case STAGE_BOIL:
// 沸腾阶段:维持高功率,但不超过最大
if (current_temp < 100) {
power = max_power;
} else {
power = max_power * 0.7f; // 维持沸腾
}
break;
case STAGE_CRUST:
// 锅巴阶段:需要精确控制,防止烧焦
static float prev_temp_crust = 0;
power = fuzzy_control(target_temp, current_temp, &prev_temp_crust);
break;
case STAGE_KEEP_WARM:
// 保温:间歇加热
if (current_temp < target_temp - 3) {
power = 20;
} else if (current_temp > target_temp + 3) {
power = 0;
} else {
power = 5; // 微弱维持
}
break;
default:
power = pid_compute(&g_pid, current_temp);
break;
}
// 功率限制
if (power > max_power) power = max_power;
if (power < 0) power = 0;
return power;
}
安全保护机制
多重保护
c
/**
* 错误代码
*/
typedef enum {
ERR_NONE = 0,
ERR_OVER_TEMP, // 过温
ERR_SENSOR_OPEN, // 传感器开路
ERR_SENSOR_SHORT, // 传感器短路
ERR_HEATER_FAIL, // 加热器故障
ERR_LID_OPEN_TIMEOUT, // 开盖超时
ERR_DRY_BURN, // 干烧
ERR_POWER_FAIL, // 电源异常
ERR_IGBT_FAIL // IGBT故障(IH)
} error_code_t;
/**
* 安全参数
*/
typedef struct {
float temp_max_bottom; // 底部最高温度
float temp_max_side; // 侧面最高温度
float temp_max_lid; // 上盖最高温度
float temp_rise_rate_max; // 最大升温速率 (°C/s)
uint16_t lid_open_timeout; // 开盖超时 (s)
float dry_burn_detect_temp; // 干烧检测温度
float dry_burn_detect_rate; // 干烧检测升温速率
} safety_params_t;
static const safety_params_t safety_params = {
.temp_max_bottom = 180.0f, // 底部不超过180°C
.temp_max_side = 150.0f,
.temp_max_lid = 120.0f,
.temp_rise_rate_max = 10.0f, // 每秒不超过10°C
.lid_open_timeout = 300, // 开盖5分钟超时
.dry_burn_detect_temp = 150.0f, // 干烧判定温度
.dry_burn_detect_rate = 5.0f // 干烧判定升温速率
};
/**
* 干烧检测
* 原理:正常煮饭时,水沸腾温度约100°C,
* 如果温度持续上升超过这个值且上升很快,说明水烧干了
*/
typedef struct {
float prev_temp;
uint32_t prev_time;
uint8_t high_temp_count;
} dry_burn_detector_t;
static dry_burn_detector_t g_dry_burn;
int detect_dry_burn(float current_temp)
{
uint32_t now = get_tick_ms();
float dt = (now - g_dry_burn.prev_time) / 1000.0f;
if (dt > 0.1f) {
float rate = (current_temp - g_dry_burn.prev_temp) / dt;
g_dry_burn.prev_temp = current_temp;
g_dry_burn.prev_time = now;
// 温度高于检测阈值且上升快
if (current_temp > safety_params.dry_burn_detect_temp &&
rate > safety_params.dry_burn_detect_rate) {
g_dry_burn.high_temp_count++;
// 连续检测到多次认为干烧
if (g_dry_burn.high_temp_count > 10) {
return 1;
}
} else {
g_dry_burn.high_temp_count = 0;
}
}
return 0;
}
/**
* 传感器故障检测
*/
int check_sensor_fault(float temp, uint16_t adc_value)
{
// ADC值接近0:短路
if (adc_value < 10) {
return ERR_SENSOR_SHORT;
}
// ADC值接近最大:开路
if (adc_value > 4085) { // 12位ADC
return ERR_SENSOR_OPEN;
}
// 温度不合理
if (temp < -30 || temp > 250) {
return ERR_SENSOR_OPEN;
}
return ERR_NONE;
}
/**
* 综合安全检查
*/
int safety_check(void)
{
// 1. 传感器检查
int err = check_sensor_fault(g_ctx.temp_bottom, adc_read(ADC_CH_BOTTOM));
if (err) return err;
err = check_sensor_fault(g_ctx.temp_side, adc_read(ADC_CH_SIDE));
if (err) return err;
// 2. 过温检查
if (g_ctx.temp_bottom > safety_params.temp_max_bottom) {
return ERR_OVER_TEMP;
}
if (g_ctx.temp_side > safety_params.temp_max_side) {
return ERR_OVER_TEMP;
}
// 3. 干烧检测(仅在煮饭模式)
if (g_ctx.state == STATE_COOKING) {
cooking_stage_t stage = g_current_recipe->stages[g_ctx.current_stage].stage;
// 吸水和沸腾阶段检测干烧
if (stage == STAGE_SOAK || stage == STAGE_BOIL) {
if (detect_dry_burn(g_ctx.temp_bottom)) {
return ERR_DRY_BURN;
}
}
}
// 4. 开盖超时检查
if (g_ctx.state == STATE_PAUSED) {
uint32_t pause_time = (get_tick_ms() - g_ctx.stage_start_time) / 1000;
if (pause_time > safety_params.lid_open_timeout) {
return ERR_LID_OPEN_TIMEOUT;
}
}
return ERR_NONE;
}
/**
* 错误处理
*/
static void action_handle_error(void)
{
// 立即关闭加热
heater_enable(0);
// 报警
buzzer_alarm();
// 显示错误信息
const char* error_msgs[] = {
[ERR_NONE] = "无错误",
[ERR_OVER_TEMP] = "E1: 温度过高",
[ERR_SENSOR_OPEN] = "E2: 传感器开路",
[ERR_SENSOR_SHORT] = "E3: 传感器短路",
[ERR_HEATER_FAIL] = "E4: 加热器故障",
[ERR_LID_OPEN_TIMEOUT] = "E5: 请合上盖子",
[ERR_DRY_BURN] = "E6: 检测到干烧",
[ERR_POWER_FAIL] = "E7: 电源异常",
[ERR_IGBT_FAIL] = "E8: IGBT故障"
};
display_show_error(error_msgs[g_ctx.error_code]);
printf("[ERROR] %s\n", error_msgs[g_ctx.error_code]);
}
用户界面与交互
按键处理
c
/**
* 按键定义
*/
typedef enum {
KEY_NONE,
KEY_POWER, // 电源/取消
KEY_START, // 开始/确认
KEY_MODE, // 模式选择
KEY_TIMER, // 预约
KEY_KEEP_WARM, // 保温
KEY_PLUS, // +
KEY_MINUS // -
} key_code_t;
/**
* 按键扫描(带消抖)
*/
static uint8_t key_state[8] = {0};
static uint8_t key_count[8] = {0};
key_code_t key_scan(void)
{
static const uint8_t key_pins[] = {
PIN_KEY_POWER, PIN_KEY_START, PIN_KEY_MODE,
PIN_KEY_TIMER, PIN_KEY_WARM, PIN_KEY_PLUS, PIN_KEY_MINUS
};
key_code_t pressed = KEY_NONE;
for (int i = 0; i < 7; i++) {
uint8_t current = gpio_read(key_pins[i]) == 0; // 低电平有效
if (current && !key_state[i]) {
key_count[i]++;
if (key_count[i] >= 3) { // 30ms消抖
key_state[i] = 1;
pressed = (key_code_t)(i + 1);
}
} else if (!current) {
key_state[i] = 0;
key_count[i] = 0;
}
}
return pressed;
}
/**
* 按键事件处理
*/
void handle_key_event(key_code_t key)
{
switch (g_ctx.state) {
case STATE_IDLE:
if (key == KEY_MODE) {
// 循环切换模式
g_ctx.mode = (g_ctx.mode + 1) % MODE_COUNT;
display_show_mode(mode_names[g_ctx.mode]);
state_machine_process(EVENT_MODE_SELECT);
}
break;
case STATE_STANDBY:
switch (key) {
case KEY_START:
state_machine_process(EVENT_START);
break;
case KEY_MODE:
g_ctx.mode = (g_ctx.mode + 1) % MODE_COUNT;
display_show_mode(mode_names[g_ctx.mode]);
break;
case KEY_TIMER:
enter_timer_setting();
break;
case KEY_POWER:
state_machine_process(EVENT_CANCEL);
break;
default:
break;
}
break;
case STATE_COOKING:
if (key == KEY_POWER) {
// 确认取消
if (confirm_cancel()) {
state_machine_process(EVENT_CANCEL);
}
}
break;
case STATE_KEEP_WARM:
if (key == KEY_POWER) {
state_machine_process(EVENT_CANCEL);
}
break;
case STATE_COMPLETE:
if (key == KEY_KEEP_WARM) {
state_machine_process(EVENT_START);
} else if (key == KEY_POWER) {
state_machine_process(EVENT_CANCEL);
}
break;
case STATE_ERROR:
if (key == KEY_POWER) {
state_machine_process(EVENT_CANCEL);
}
break;
default:
break;
}
}
显示更新
c
/**
* 显示内容
*/
typedef struct {
char line1[20]; // 第一行(模式/状态)
char line2[20]; // 第二行(时间/温度)
uint8_t icon_heat; // 加热图标
uint8_t icon_warm; // 保温图标
uint8_t icon_timer; // 预约图标
} display_content_t;
/**
* 更新烹饪显示
*/
void update_cooking_display(void)
{
display_content_t disp;
// 第一行:模式名称
snprintf(disp.line1, sizeof(disp.line1), "%s",
g_current_recipe->name);
// 第二行:剩余时间
uint32_t elapsed = (get_tick_ms() - g_ctx.cook_start_time) / 1000;
uint32_t total = g_current_recipe->total_time_min * 60;
int32_t remaining = total - elapsed;
if (remaining < 0) remaining = 0;
snprintf(disp.line2, sizeof(disp.line2), "%02d:%02d",
remaining / 60, remaining % 60);
// 图标
disp.icon_heat = (g_ctx.target_power > 0);
disp.icon_warm = (g_ctx.state == STATE_KEEP_WARM);
disp.icon_timer = g_ctx.timer_enabled;
display_update(&disp);
}
/**
* 数码管/LCD显示驱动
*/
void display_update(display_content_t *content)
{
// 假设使用段码LCD
lcd_clear();
lcd_set_cursor(0, 0);
lcd_print(content->line1);
lcd_set_cursor(1, 0);
lcd_print(content->line2);
// 更新图标
lcd_set_icon(ICON_HEAT, content->icon_heat);
lcd_set_icon(ICON_WARM, content->icon_warm);
lcd_set_icon(ICON_TIMER, content->icon_timer);
}
完整主程序
c
/**
* main.c - 电饭锅主程序
*/
#include "cooker.h"
#include "display.h"
#include "heater.h"
#include "sensor.h"
#include "buzzer.h"
/**
* 系统初始化
*/
void system_init(void)
{
// 时钟初始化
clock_init();
// GPIO初始化
gpio_init();
// ADC初始化(温度传感器)
adc_init();
// 定时器初始化
timer_init();
// 显示初始化
display_init();
// 加热器初始化
heater_init();
// 蜂鸣器初始化
buzzer_init();
// 状态初始化
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.state = STATE_IDLE;
g_ctx.mode = MODE_STANDARD_RICE;
// 显示开机画面
display_show_logo();
delay_ms(1000);
// 开机自检
if (self_test() != 0) {
g_ctx.state = STATE_ERROR;
display_show_error("自检失败");
return;
}
display_show_idle();
printf("[SYSTEM] Initialized\n");
}
/**
* 开机自检
*/
int self_test(void)
{
printf("[TEST] Starting self-test...\n");
// 检测传感器
float temp = read_temperature(SENSOR_BOTTOM);
if (temp < -20 || temp > 100) {
printf("[TEST] Bottom sensor fail: %.1f\n", temp);
g_ctx.error_code = ERR_SENSOR_OPEN;
return -1;
}
temp = read_temperature(SENSOR_SIDE);
if (temp < -20 || temp > 100) {
printf("[TEST] Side sensor fail: %.1f\n", temp);
g_ctx.error_code = ERR_SENSOR_OPEN;
return -1;
}
// 可以加入加热器测试(短暂通电检测电流)
// ...
printf("[TEST] Self-test passed\n");
return 0;
}
/**
* 10ms定时器中断
*/
void TIM3_IRQHandler(void)
{
if (timer_get_flag(TIM3)) {
timer_clear_flag(TIM3);
static uint8_t tick_10ms = 0;
tick_10ms++;
// 10ms: 按键扫描
key_code_t key = key_scan();
if (key != KEY_NONE) {
handle_key_event(key);
}
// 100ms: 温度控制和烹饪逻辑
if (tick_10ms >= 10) {
tick_10ms = 0;
cooking_process();
// 安全检查
int err = safety_check();
if (err != ERR_NONE) {
g_ctx.error_code = err;
state_machine_process(EVENT_TEMP_ALARM);
}
}
}
}
/**
* 主函数
*/
int main(void)
{
system_init();
while (1) {
// 主循环可以处理低优先级任务
// 空闲时降低功耗
if (g_ctx.state == STATE_IDLE) {
enter_low_power_mode();
}
// 调试输出
#ifdef DEBUG
static uint32_t last_debug = 0;
if (get_tick_ms() - last_debug > 1000) {
last_debug = get_tick_ms();
printf("[DBG] State:%d Stage:%d Temp:%.1f/%.1f/%.1f Power:%d%%\n",
g_ctx.state, g_ctx.current_stage,
g_ctx.temp_bottom, g_ctx.temp_side, g_ctx.temp_lid,
g_ctx.target_power);
}
#endif
}
}
调试技巧
温度曲线记录
c
/**
* 温度曲线记录(用于调试和优化配方)
*/
#define LOG_SIZE 1000
typedef struct {
uint32_t timestamp;
float temp_bottom;
float temp_side;
uint8_t power;
uint8_t stage;
} temp_log_t;
static temp_log_t g_temp_log[LOG_SIZE];
static uint16_t g_log_index = 0;
void log_temperature(void)
{
if (g_log_index >= LOG_SIZE) return;
g_temp_log[g_log_index].timestamp = get_tick_ms() - g_ctx.cook_start_time;
g_temp_log[g_log_index].temp_bottom = g_ctx.temp_bottom;
g_temp_log[g_log_index].temp_side = g_ctx.temp_side;
g_temp_log[g_log_index].power = g_ctx.target_power;
g_temp_log[g_log_index].stage = g_ctx.current_stage;
g_log_index++;
}
void dump_temperature_log(void)
{
printf("Time,TempBottom,TempSide,Power,Stage\n");
for (int i = 0; i < g_log_index; i++) {
printf("%u,%.1f,%.1f,%d,%d\n",
g_temp_log[i].timestamp,
g_temp_log[i].temp_bottom,
g_temp_log[i].temp_side,
g_temp_log[i].power,
g_temp_log[i].stage);
}
}
配方参数在线调整
c
/**
* 串口命令调试
*/
void debug_command_handler(char *cmd)
{
float value;
if (sscanf(cmd, "SET_TEMP %f", &value) == 1) {
g_pid.target = value;
printf("Target temp set to %.1f\n", value);
}
else if (sscanf(cmd, "SET_KP %f", &value) == 1) {
g_pid.kp = value;
printf("Kp set to %.2f\n", value);
}
else if (sscanf(cmd, "SET_KI %f", &value) == 1) {
g_pid.ki = value;
printf("Ki set to %.2f\n", value);
}
else if (sscanf(cmd, "SET_KD %f", &value) == 1) {
g_pid.kd = value;
printf("Kd set to %.2f\n", value);
}
else if (strcmp(cmd, "DUMP_LOG") == 0) {
dump_temperature_log();
}
else if (strcmp(cmd, "STATUS") == 0) {
printf("State:%d Mode:%d Stage:%d\n",
g_ctx.state, g_ctx.mode, g_ctx.current_stage);
printf("Temp: B=%.1f S=%.1f L=%.1f\n",
g_ctx.temp_bottom, g_ctx.temp_side, g_ctx.temp_lid);
printf("PID: Kp=%.2f Ki=%.2f Kd=%.2f\n",
g_pid.kp, g_pid.ki, g_pid.kd);
}
}
总结
智能电饭锅控制系统的核心技术点:
| 模块 | 关键技术 |
|---|---|
| 温度测量 | NTC热敏电阻、ADC采样、温度换算 |
| 加热控制 | PWM调功、IH电磁加热、功率调节 |
| 烹饪配方 | 多阶段温度曲线、时间/温度条件 |
| 状态机 | 多状态切换、事件驱动、异常处理 |
| 温度控制 | PID算法、模糊控制、分阶段调参 |
| 安全保护 | 过温、干烧、传感器故障检测 |
| 用户交互 | 按键扫描、显示更新、预约功能 |
各烹饪模式的关键差异:
| 模式 | 关键特点 |
|---|---|
| 标准煮饭 | 充分吸水 + 沸腾 + 焖饭 |
| 柴火饭 | 短吸水 + 强火 + 高温结锅巴 |
| 煲仔饭 | 中途加料 + 大火收汁 + 锅巴 |
| 煮粥 | 长时间 + 小火 + 防溢出 |
| 蛋糕 | 恒温蒸制 + 精确控温 |
| 酸奶 | 低温 + 长时间 + 恒温发酵 |
这套代码框架在实际项目中经过验证,可以直接参考使用。关键是要根据实际硬件调整温度传感器参数和PID系数。
有问题欢迎评论区交流~
参考资料:
- 《智能家电控制技术》
- 美的/苏泊尔电饭锅专利文献
- 《模糊控制技术》