Có. Và firmware của bếp từ thực tế còn thú vị hơn firmware nồi cơm, vì nó phải điều khiển một hệ thống công suất lớn với IGBT/MOSFET đóng cắt ở tần số cao, đồng thời liên tục theo dõi dòng điện, điện áp, nhiệt độ và trạng thái nồi.
Điểm rất hay là bếp từ đơn, bếp từ đôi và bếp hồng ngoại có chung một “bộ não” firmware ở tầng trên, nhưng tầng điều khiển công suất khác nhau.
1. Trước hết hãy nhìn toàn bộ hệ thống
Một bếp từ có thể khái quát:
NGƯỜI DÙNG
│
Phím / Touch
│
▼
┌─────────────┐
│ MCU │
└──────┬──────┘
│
┌─────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Sensor Display Buzzer
│
│
├── Nhiệt độ
├── Dòng điện
├── Điện áp
├── Nhiệt IGBT
└── Phát hiện nồi
│
▼
POWER CONTROL
│
▼
Gate Driver
│
▼
IGBT
│
▼
Resonant LC
│
▼
Cuộn dây
│
▼
Đáy nồi
Đây chính là closed-loop control.
2. Firmware bếp từ thực chất làm 6 việc lớn
Tôi thường chia nó thành:
1. USER INTERFACE
↓
2. STATE MACHINE
↓
3. PAN DETECTION
↓
4. POWER CONTROL
↓
5. SENSOR / FEEDBACK
↓
6. SAFETY / PROTECTION
Và tất cả chạy liên tục.
3. Khi vừa cắm điện
MCU không lập tức bật IGBT.
Nó sẽ trải qua một chuỗi kiểu:
POWER ON
↓
MCU INIT
↓
ADC INIT
↓
TIMER INIT
↓
GPIO INIT
↓
DISPLAY INIT
↓
SELF CHECK
↓
CHECK SENSOR
↓
WAIT
Sau đó:
┌───────────┐
│ STANDBY │
└─────┬─────┘
│
User presses
│
▼
SELECT MODE
│
▼
CHECK PAN
│
┌───────┴────────┐
│ │
NO PAN PAN OK
│ │
▼ ▼
WAIT PAN START POWER
4. Phát hiện nồi là một phần rất quan trọng
Đây là điểm bếp từ khác bếp điện trở.
Bếp từ phải biết:
“Có nồi phù hợp đặt trên vùng nấu hay không?”
Firmware có thể kích hệ thống ở mức kiểm tra rất nhỏ, sau đó quan sát phản hồi điện:
IGBT / coil
↓
Tạo từ trường
↓
Quan sát dòng điện / đáp ứng mạch cộng hưởng
↓
MCU ADC
↓
Pan detection
Nếu:
NO PAN
thì:
IGBT = OFF
Nếu:
PAN DETECTED
thì mới cho phép quá trình điều khiển công suất tiếp tục.
5. Đây mới là “trái tim” của bếp từ
Giả sử người dùng chọn:
POWER = 1200 W
MCU không đơn giản nói:
IGBT = ON;
Bởi vì IGBT phải đóng cắt rất nhanh.
Firmware phải tạo tín hiệu điều khiển:
MCU
↓
Timer / PWM
↓
Gate Driver
↓
IGBT
Ví dụ khái quát:
┌──────────────┐
│ Power target │
│ 1200 W │
└──────┬───────┘
↓
Control algorithm
↓
Gate drive command
↓
IGBT
↓
Heater
↓
Measure power
│
└──────────► Feedback
Đó chính là vòng kín.
6. MCU phải biết “thực tế đang tiêu thụ bao nhiêu công suất”
Ví dụ người dùng yêu cầu:
1200 W
nhưng hệ thống thực tế có thể đang:
1500 W
Firmware phát hiện:
P_actual > P_target
→ giảm năng lượng đưa vào.
Ngược lại:
P_actual < P_target
→ tăng.
Khái quát:
Target Power
│
▼
┌──────────────┐
│ Controller │
└──────┬───────┘
│
▼
IGBT drive
│
▼
Induction
│
▼
Actual Power
│
└──────► Feedback
7. Nhưng bếp từ không chỉ nhìn công suất
Nó còn nhìn rất nhiều thứ:
ADC
│
├── DC bus voltage
│
├── Current
│
├── Coil temperature
│
├── IGBT temperature
│
├── Pot temperature
│
└── Other sensors
Ví dụ:
Temperature IGBT = 75°C
→ bình thường.
Nhưng:
Temperature IGBT = 105°C
→ giảm công suất.
Temperature IGBT = 120°C
→ shutdown.
8. Vì vậy thuật toán thực tế giống thế này
TARGET POWER
│
▼
POWER CONTROL
│
▼
IGBT DRIVE
│
▼
HEATING
│
▼
SENSOR READ
│
┌────────┼────────┐
▼ ▼ ▼
Current Voltage Temp
│ │ │
└────────┼────────┘
▼
MCU ADC
│
▼
CONTROL DECISION
│
┌────────┼─────────┐
▼ ▼ ▼
Increase Hold Decrease
power power
9. Bếp từ đơn tương đối đơn giản
Bếp đơn:
MCU
│
▼
Controller
│
▼
IGBT
│
▼
Coil #1
Firmware chỉ cần điều khiển:
Zone 1
Ví dụ State Machine:
OFF
│
▼
STANDBY
│
▼
PAN CHECK
│
▼
HEATING
│
├── Increase power
├── Maintain
├── Decrease
│
├── No pan
│
├── Over temperature
│
└── Over current
│
▼
ERROR
10. Bếp từ đôi thú vị hơn rất nhiều
Bếp đôi:
MCU
│
┌───────┴────────┐
▼ ▼
Controller A Controller B
│ │
IGBT A IGBT B
│ │
Coil A Coil B
Nhưng hai vùng nấu thường không hoàn toàn độc lập về nguồn điện.
Ví dụ:
AC 220V
│
Rectifier
│
DC BUS
│
├──────────► Zone A
│
└──────────► Zone B
Giả sử:
Zone A = 1800 W
Zone B = 1800 W
Tổng:
3600 W
nhưng nguồn hoặc thiết kế bếp có thể không cho phép mức đó.
Firmware phải làm:
POWER MANAGEMENT
Ví dụ:
A yêu cầu 1800 W
B yêu cầu 1800 W
Total request = 3600 W
Maximum allowed = 3000 W
→ Firmware phân bổ:
A = 1800 W
B = 1200 W
Đây là một scheduler/power allocator.
11. Đây chính là lý do bếp đôi thường có “chia công suất”
Bạn có thể bắt gặp logic kiểu:
TOTAL POWER LIMIT
│
▼
POWER MANAGER
/ \
/ \
▼ ▼
Zone A Zone B
1800W 600W
Nếu người dùng tăng Zone B:
Zone B: 600 → 1200 W
firmware có thể giảm Zone A:
Zone A: 1800 → 1200 W
để:
A + B ≤ POWER_LIMIT
Đây là một bài toán điều khiển rất thú vị.
12. Bếp hồng ngoại thì khác
Bếp hồng ngoại không cần phát hiện nồi bằng nguyên lý cảm ứng.
Phần nhiệt thường là:
AC
│
▼
Heating element
│
▼
Infrared radiation
│
▼
Glass
│
▼
Pot
Firmware chủ yếu điều khiển:
ON/OFF
hoặc điều khiển công suất bằng:
Triac
và zero-cross/phase control tùy thiết kế.
Ví dụ:
Target = 1200 W
100% → 1200 W
75% → 900 W
50% → 600 W
25% → 300 W
Một cách đơn giản là time-proportional control:
10 giây:
██████████ = 100%
███████░░░ = 70%
█████░░░░░ = 50%
██░░░░░░░░ = 20%
13. Bếp hồng ngoại cũng có closed-loop
Ví dụ:
User selects 600°C
↓
MCU
↓
Read NTC / thermocouple
↓
Actual = 450°C
↓
Need more heat
↓
Heater ON
Sau đó:
Actual = 580°C
↓
Reduce power
và:
Actual = 610°C
↓
Heater OFF / reduce
14. Một điểm cực kỳ quan trọng: bảo vệ không nằm “sau” điều khiển
Nó phải hoạt động song song.
Không phải:
Control
↓
Protection
mà gần hơn là:
┌─────────────┐
│ │
▼ │
CONTROL │
│ │
▼ │
IGBT │
│ │
▼ │
HEATER │
│ │
▼ │
SENSOR │
│ │
▼ │
MCU ◄─────────────┘
▲
│
SAFETY
Safety có quyền cắt công suất ngay lập tức.
15. Firmware có thể có các mức bảo vệ
Ví dụ:
OVP → Over Voltage Protection
OCP → Over Current Protection
OTP → Over Temperature Protection
UVP → Under Voltage Protection
OP → Over Power
NP → No Pan
NTC → Sensor fault
IGBT → IGBT fault
Khi có lỗi nghiêm trọng:
ERROR
↓
Disable gate
↓
IGBT OFF
↓
Display error
↓
Buzzer
↓
Wait for reset/recovery
16. Firmware bếp từ có thể được tổ chức như thế này
Nếu viết theo phong cách STM32:
while (1)
{
Key_Scan();
ADC_Process();
Pan_Detection();
Power_Control();
Temperature_Control();
Power_Manager();
Safety_Check();
Display_Update();
Buzzer_Process();
}
Nhưng phần IGBT gate drive và protection thời gian rất ngắn có thể được xử lý bằng timer/PWM, interrupt hoặc phần cứng chuyên dụng thay vì chờ vòng while(1).
17. Nếu đưa về kiến thức STM32 mà bạn đang học
Bạn sẽ thấy bếp từ là một “bài tập firmware cực lớn”:
| Chức năng | STM32 có thể dùng |
|---|---|
| Đọc NTC | ADC |
| Đọc dòng | ADC |
| Đọc điện áp | ADC |
| Điều khiển IGBT | Timer/PWM |
| Zero-cross | GPIO/EXTI/Timer |
| Phát hiện nồi | ADC + timer/control |
| Phím | GPIO |
| Màn hình | GPIO/I2C/SPI/UART |
| Buzzer | Timer/GPIO |
| Timing | SysTick/Timer |
| Watchdog | IWDG/WWDG |
| Fault | Interrupt |
| Power management | Software |
| State machine | C |
Cho nên nếu bạn hiểu được bếp từ, bạn gần như đã chạm vào rất nhiều kiến thức cốt lõi của firmware embedded.
18. Và có một điều tôi muốn đặc biệt nhấn mạnh
Nếu chúng ta muốn học firmware bếp từ một cách bài bản, tôi không nghĩ nên bắt đầu ngay bằng code.
Ta nên đi theo đúng con đường của một kỹ sư thiết kế firmware:
BẾP TỪ
│
▼
① YÊU CẦU SẢN PHẨM
│
▼
② STATE MACHINE
│
▼
③ SENSOR MODEL
│
▼
④ POWER CONTROL
│
▼
⑤ SAFETY MODEL
│
▼
⑥ TIMING / INTERRUPT
│
▼
⑦ SOFTWARE ARCHITECTURE
│
▼
⑧ C CODE
│
▼
⑨ TEST / FAULT INJECTION
Đây cũng chính là cách chúng ta có thể chuyển từ việc bạn đang học những thứ nhỏ như GPIO → Timer → ADC → UART → Interrupt sang tư duy thiết kế một firmware sản phẩm hoàn chỉnh.
Nếu bạn muốn đi sâu, tôi có thể lấy một bếp từ đơn 2000 W làm ví dụ, rồi cùng bạn từ con số 0 thiết kế một firmware hoàn chỉnh trên STM32F103: tôi sẽ vẽ cho bạn sơ đồ khối → State Machine → thuật toán phát hiện nồi → thuật toán điều khiển công suất → ADC đọc dòng/NTC → Timer tạo PWM → bảo vệ OCP/OTP/OVP → watchdog → cuối cùng mới viết main.c.
Thiết kế một firmware hoàn chỉnh trên STM32F103 từ con số 0
Được. Chúng ta sẽ làm theo kiểu một dự án firmware sản phẩm thực tế, nhưng trước tiên tôi muốn đặt một ranh giới an toàn: phần firmware dưới đây sẽ thiết kế cho mô hình học tập/bench prototype điện áp thấp. Không nên dùng STM32F103 trực tiếp để điều khiển mạch 220 VAC/IGBT công suất nếu chưa có thiết kế cách ly, gate driver, bảo vệ phần cứng và quy trình kiểm thử phù hợp.
Và vì bạn vừa nhắc đến interrupt, tôi sẽ đưa interrupt vào đúng vị trí của nó thay vì biến mọi thứ thành một vòng while(1).
1. Mục tiêu của dự án
Ta giả lập một bếp từ đơn 2000 W với STM32F103C8T6:
┌──────────────────────┐
│ STM32F103 │
│ │
User ── Keys ─────►│ GPIO │
│ │
NTC ──────────────►│ ADC │
│ │
Current sensor ───►│ ADC │
│ │
Voltage sensor ───►│ ADC │
│ │
│ Timer/PWM ──────────►│── Gate driver
│ │
│ UART ───────────────►│── PC
│ │
│ IWDG │
└──────────────────────┘
Mục tiêu phần mềm:
- OFF
- STANDBY
- PAN DETECTION
- HEATING
- POWER CONTROL
- OVER-TEMPERATURE
- OVER-CURRENT
- NO-PAN
- ERROR
- COOLING
- STOP
2. Trước tiên phải hiểu một nguyên tắc
Đừng viết:
while(1)
{
đọc nhiệt độ;
điều khiển IGBT;
đọc phím;
...
}
rồi nhét tất cả vào đó.
Firmware tốt hơn sẽ chia thành:
HARDWARE
│
┌─────────┼─────────┐
▼ ▼ ▼
Timer ADC GPIO
│ │ │
└─────────┼─────────┘
▼
INTERRUPTS
│
▼
DATA / EVENTS
│
▼
STATE MACHINE
│
┌─────────┼─────────┐
▼ ▼ ▼
Control Safety UI
Interrupt không phải là thuật toán điều khiển.
Interrupt chỉ là cơ chế giúp MCU phản ứng đúng lúc với một sự kiện.
3. Ví dụ cực kỳ đơn giản về interrupt
Giả sử timer tạo interrupt mỗi 1 ms:
Timer
│
│ 1 ms
▼
Interrupt
│
▼
System tick++
Ta có:
volatile uint32_t system_ms = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
system_ms++;
}
}
Sau đó main() không cần tự đếm thời gian bằng delay.
if (system_ms - last_time >= 10)
{
last_time = system_ms;
Key_Scan();
}
Như vậy:
TIM2 interrupt
│
▼
system_ms
│
├── 10 ms → Key scan
├── 100 ms → Temperature control
├── 500 ms → Display
└── 1000 ms → Cooking timer
Đây là tư duy rất quan trọng.
4. Kiến trúc firmware chúng ta sẽ xây
Tôi đề xuất cấu trúc:
Core/
│
├── Inc/
│ ├── cooker.h
│ ├── control.h
│ ├── safety.h
│ ├── sensor.h
│ ├── key.h
│ └── display.h
│
└── Src/
├── main.c
├── cooker.c
├── control.c
├── safety.c
├── sensor.c
├── key.c
└── display.c
Đây chính là cách một project bắt đầu trở thành firmware architecture, thay vì một file main.c khổng lồ.
5. Trung tâm của firmware: State Machine
Ta định nghĩa:
typedef enum
{
STATE_OFF = 0,
STATE_STANDBY,
STATE_PAN_CHECK,
STATE_HEATING,
STATE_COOLING,
STATE_ERROR
} CookerState_t;
Và:
volatile CookerState_t cooker_state = STATE_OFF;
Sau đó:
void Cooker_Task(void)
{
switch (cooker_state)
{
case STATE_OFF:
Cooker_Off();
break;
case STATE_STANDBY:
Cooker_Standby();
break;
case STATE_PAN_CHECK:
Cooker_PanCheck();
break;
case STATE_HEATING:
Cooker_Heating();
break;
case STATE_COOLING:
Cooker_Cooling();
break;
case STATE_ERROR:
Cooker_Error();
break;
}
}
Đây là trái tim của firmware.
6. Luồng trạng thái
POWER ON
│
▼
SELF TEST
│
▼
STANDBY
│
START COOK
│
▼
PAN CHECK
/ \
NO PAN PAN OK
│ │
▼ ▼
WAIT HEATING
│
┌──────────────┼───────────────┐
│ │ │
▼ ▼ ▼
Temperature Current User STOP
high high │
│ │ ▼
▼ ▼ COOLING
ERROR ERROR
│
▼
ERROR
7. Bây giờ đến phần Sensor
Ta có:
typedef struct
{
uint16_t adc_ntc;
uint16_t adc_current;
uint16_t adc_voltage;
float temperature;
float current;
float voltage;
} SensorData_t;
và:
SensorData_t sensor;
ADC sẽ cập nhật dữ liệu.
Ví dụ:
void Sensor_Process(void)
{
sensor.adc_ntc = ADC_Read_NTC();
sensor.adc_current = ADC_Read_Current();
sensor.adc_voltage = ADC_Read_Voltage();
sensor.temperature = NTC_To_Temperature(sensor.adc_ntc);
sensor.current = ADC_To_Current(sensor.adc_current);
sensor.voltage = ADC_To_Voltage(sensor.adc_voltage);
}
8. Một điều rất quan trọng: ADC không nên được đọc tùy tiện
Với hệ thống điều khiển công suất, chúng ta có thể dùng:
ADC
│
├── software polling
│
├── interrupt
│
└── DMA
Nếu có nhiều kênh ADC, DMA rất hữu ích:
ADC
│
├── Channel 1 → NTC
├── Channel 2 → Current
└── Channel 3 → Voltage
│
▼
DMA
│
▼
RAM buffer
CPU không phải liên tục:
ADC_Read();
ADC_Read();
ADC_Read();
DMA làm việc đó.
Đây chính là mối liên hệ với câu hỏi trước của bạn về DMA giống như một “tay sai” cho CPU.
9. Power Controller
Giả sử người dùng yêu cầu:
Power target = 1000 W
Ta có:
typedef struct
{
float target_power;
float actual_power;
uint8_t output_percent;
} PowerControl_t;
Thuật toán đơn giản ban đầu:
void Power_Control(void)
{
float error;
power.actual_power =
sensor.voltage * sensor.current;
error =
power.target_power -
power.actual_power;
if (error > 100)
{
power.output_percent += 5;
}
else if (error < -100)
{
power.output_percent -= 5;
}
if (power.output_percent > 100)
power.output_percent = 100;
}
Đây chưa phải thuật toán điều khiển bếp từ thương mại. Nó chỉ là bộ điều khiển đơn giản để chúng ta xây dựng kiến trúc.
10. Sau đó Output Controller mới điều khiển phần công suất
void Power_Output_Task(void)
{
if (cooker_state == STATE_HEATING)
{
PWM_SetPower(power.output_percent);
}
else
{
PWM_SetPower(0);
}
}
Điểm cực kỳ quan trọng:
CONTROL quyết định:
"cần bao nhiêu công suất?"
OUTPUT quyết định:
"phát lệnh phần cứng như thế nào?"
Hai thứ này nên tách nhau.
11. Safety phải có quyền cao hơn Control
Đây là kiến trúc rất quan trọng:
POWER CONTROL
│
▼
Requested Power
│
▼
SAFETY CHECK
│
┌───────┴────────┐
│ │
SAFE FAULT
│ │
▼ ▼
OUTPUT POWER OUTPUT OFF
Ví dụ:
void Safety_Process(void)
{
if (sensor.temperature > MAX_TEMP)
{
fault = FAULT_OVER_TEMP;
cooker_state = STATE_ERROR;
}
if (sensor.current > MAX_CURRENT)
{
fault = FAULT_OVER_CURRENT;
cooker_state = STATE_ERROR;
}
}
Và:
void Power_Output_Task(void)
{
if (cooker_state == STATE_ERROR)
{
PWM_SetPower(0);
return;
}
PWM_SetPower(power.output_percent);
}
12. Nhưng bảo vệ quan trọng không nên chỉ dựa vào software
Đây là điểm rất quan trọng đối với bếp thật.
Ví dụ:
Current sensor
│
├────────► ADC → Software
│
└────────► Hardware comparator
│
▼
Gate shutdown
Nếu dòng quá lớn:
OCP
│
▼
Hardware
│
▼
Disable gate
không cần chờ:
ADC → CPU → while loop
Đối với mạch công suất cao, hardware protection và software protection nên tồn tại đồng thời.
13. Main loop cuối cùng sẽ rất sạch
Ví dụ:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
Sensor_Init();
Control_Init();
Safety_Init();
Cooker_Init();
HAL_TIM_Base_Start_IT(&htim2);
while (1)
{
Sensor_Task();
Safety_Task();
Cooker_Task();
Control_Task();
Power_Output_Task();
Key_Task();
Display_Task();
HAL_IWDG_Refresh(&hiwdg);
}
}
Bạn thấy điều gì?
main() không chứa thuật toán chi tiết.
Nó giống một người quản lý:
Sensor_Task()
Safety_Task()
Cooker_Task()
Control_Task()
Power_Output_Task()
Key_Task()
Display_Task()
14. Còn interrupt làm việc phía dưới
Ví dụ:
TIM2
│
1 ms
│
▼
Timer Interrupt
│
▼
system_ms++
ADC DMA:
ADC
│
conversion
│
▼
DMA
│
▼
RAM
│
▼
Sensor_Process()
Nút nhấn nếu cần phản ứng tức thời có thể dùng EXTI:
BUTTON
│
▼
GPIO EXTI
│
▼
Interrupt
│
▼
Set event flag
Ví dụ:
volatile uint8_t key_event = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == KEY_START_Pin)
{
key_event = 1;
}
}
Sau đó không nên xử lý cả một đống logic trong ISR.
ISR chỉ:
Hardware event
↓
Set flag
↓
Exit interrupt
Còn:
if (key_event)
{
key_event = 0;
Process_Start_Key();
}
được xử lý trong main loop.
Đây là một nguyên tắc firmware rất quan trọng:
ISR càng ngắn càng tốt; ISR báo sự kiện, còn application xử lý sự kiện.
15. Toàn bộ firmware lúc này có thể hình dung như một hệ thống
STM32F103
│
┌───────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
INTERRUPT MAIN LOOP DMA
│ │ │
┌────┼────┐ ┌──────┼──────┐ │
│ │ │ │ │ │ │
Timer EXTI ADC Sensor Safety Control ADC
│ │ │ │ │ │ │
└────┴────┘ └──────┴──────┘ │
│ │ │
▼ ▼ │
Events State Machine ◄────────────┘
│
▼
Power Control
│
▼
PWM/TIMER
│
▼
Gate Driver
│
▼
Power Stage
Đây chính là kiến trúc firmware mà tôi muốn chúng ta xây.
16. Và tôi đề xuất chúng ta không viết toàn bộ code ngay
Nếu viết ngay vài nghìn dòng code thì bạn sẽ rất khó hiểu.
Chúng ta sẽ xây giống như xây một sản phẩm thật:
Giai đoạn 1 — MCU foundation
STM32F103
│
├── GPIO
├── Timer
├── Interrupt
├── ADC
├── DMA
├── UART
└── Watchdog
Giai đoạn 2 — Firmware framework
main loop
↓
scheduler
↓
tasks
Giai đoạn 3 — Sensor
NTC
Current
Voltage
Giai đoạn 4 — State Machine
OFF
STANDBY
PAN CHECK
HEATING
COOLING
ERROR
Giai đoạn 5 — Control
Target
↓
Controller
↓
PWM
Giai đoạn 6 — Safety
OTP
OCP
OVP
Sensor fault
Watchdog
Giai đoạn 7 — Test
Normal
↓
No pan
↓
NTC disconnected
↓
Over temperature
↓
Over current
↓
MCU hang
Và đặc biệt, tôi muốn chúng ta làm từng bước trên chính STM32F103C8T6 của bạn
Bước đầu tiên tôi đề nghị chưa đụng đến 220 V, IGBT hay cuộn dây.
Chúng ta sẽ tạo một “bếp từ giả lập” trên Blue Pill:
STM32F103
│
┌──────────────┼──────────────┐
│ │ │
ADC GPIO UART
│ │ │
▼ ▼ ▼
Potentiometer Buttons PC
= Nhiệt độ = START Monitor
= Dòng = STOP
= Điện áp = POWER
│
▼
State Machine
│
▼
PWM LED
│
▼
"Heater giả"
Sau đó trên UART bạn sẽ nhìn thấy kiểu:
[BOOT]
SELF TEST OK
STATE = STANDBY
START
PAN DETECTED
STATE = HEATING
TEMP = 45.2 C
POWER TARGET = 800 W
OUTPUT = 42 %
TEMP = 63.8 C
POWER TARGET = 800 W
OUTPUT = 31 %
TEMP = 95.4 C
SAFETY: OK
TEMP = 120.5 C
FAULT: OVER TEMPERATURE
OUTPUT = 0 %
STATE = ERROR
Khi mô hình này chạy đúng, chúng ta mới thay “heater giả + potentiometer” bằng các khối cảm biến/công suất thực tế.
Như vậy bạn sẽ hiểu được toàn bộ chuỗi:
Interrupt → Timer → ADC → DMA → State Machine → Control Algorithm → PWM → Safety → Watchdog
thay vì chỉ copy một đoạn main.c rồi không hiểu tại sao nó hoạt động.

