ARTICLE · 1271054
【软件和硬件介绍 】基于STM32的智能洗碗机|自动洗涤、WiFi远程控制、多模式运行

🤞大家好,这里是5132单片机毕设项目分享,今天给大家分享的是《基于STM32的智能洗碗机设计》!
实物购买方式见文末!
软件和硬件介绍
——————————————————
基于 STM32 的智能洗碗机
硬件设计 · 软件架构 · 程序设计思路
版本:V2.0
第一部分硬件介绍
1硬件总体方案
整机硬件由「一块自制主控 PCB + 一组外置执行器 + 两路传感器」构成。主控 PCB 以 STM32F103C8T6 最小系统板为插件核心,板上集成电源输入与滤波、OLED 与按键的人机接口、传感器输入接口、三路继电器驱动电路、TB6612FNG 电机驱动、以及 WiFi、风扇、消毒灯、蜂鸣器接口。外置部分为排水泵、补水泵、加热模块、直流电机(喷淋臂)、5W 紫外消毒灯与散热风扇。
这样划分的好处是:主控板只负责「弱电控制」,所有大电流、市电相关的器件全部外置,通过接线端子和继电器隔离。既降低了 PCB 的布线难度与发热风险,也方便替换不同功率的执行器——换一个更大功率的加热模块,只需换继电器后端,主控板不用动。

图 1-1系统总体结构框图
2主控单元与最小系统
2.1为什么选 STM32F103C8T6
资源匹配:本项目需要 1 路 12 位 ADC(水位)、1 路定时器 PWM(电机调速)、1 路 USART(WiFi)、1 路 I2C(OLED),F103C8T6 提供 2 个 ADC、3 个通用定时器、3 个 USART、2 个 I2C,余量充足。
主频足够:72MHz 主频配合硬件乘除法,可以在 20ms 的按键消抖周期内完成一次完整的采集、判断与显示刷新。
生态成熟:标准外设库(SPL)资料丰富、例程齐全,便于快速搭建与后续修改;20KB SRAM 对 128×64 的 OLED 显存(1KB)绰绰有余。
成本与可获得性:最小系统板价格低、易采购,插装在自制板上便于更换与复用。
表 1-1主控芯片主要参数
参数 | STM32F103C8T6 |
内核 | ARM Cortex-M3 |
主频 | 72MHz(8MHz 外部晶振经 PLL 倍频) |
Flash / SRAM | 64KB / 20KB |
ADC | 2 个 12 位 ADC,共 10 个外部通道 |
定时器 | 4 个通用定时器(TIM2/3/4/5)+ 2 个高级定时器 + 2 个基本定时器 |
通信接口 | 3 个 USART、2 个 I2C、2 个 SPI、1 个 CAN、1 个 USB |
封装 | LQFP48 |
2.2时钟与供电
系统时钟由 8MHz 外部晶振经 PLL 9 倍频得到 72MHz;RTC 使用独立的 32.768kHz 晶振(LSE),并由 BT2 纽扣电池座上的 CR2032 单独供电,实现整机断电后继续走时。供电方面,Type-C 接口(USB6)输入 5V,经自锁开关 U40 控制整板总电源,1000μF 电解电容与 100nF 瓷片电容并联滤波——大电容吸收电机与继电器动作时的电流冲击,小电容旁路高频噪声。
3传感器检测电路
3.1DS18B20 水温检测
DS18B20 是单总线数字温度传感器,测温范围 −55~+125℃,在 12 位分辨率下精度可达 0.0625℃。它的优点是输出即数字量,不需要额外的运放或 ADC 校准,单根数据线即可完成通信,抗干扰能力强,特别适合水汽环境。
接线:VDD 接 3.3V、GND 接地、DQ 数据脚接 PB0,数据脚需 4.7kΩ 上拉电阻(板上已有)。
数据格式:原始值为 16 位有符号整数,低 4 位为小数部分,实际温度 = 原始值 × 0.0625。
程序处理:为便于比较与上报,程序内部统一按「实际温度 ×10」的整数保存,例如 23.5℃ 存储为 235,这样既保留了 0.1℃ 分辨率,又避免了浮点运算在中断中的开销。
3.2水位检测
水位采用电阻式水位传感器。其原理是:传感器正面有一排平行裸露的导电条,水浸没高度不同,导电条之间的等效电阻不同,经板上分压后输出 0~3.3V 的模拟电压。电压越高表示浸没越深、水位越高。
接线:信号脚接 PA0(ADC_Channel_0),供电 3.3V。
标定:程序将 ADC 采样值按比例映射为 0~100 的相对水位值,工程中只启用这一路 ADC 通道,其余通道保留为扩展用。
抗干扰:单次采样取多次平均,并对结果做限幅,防止水面波动导致读数跳变引起误判。
工程实现说明:本项目的传感器的抽象层(AD.c)以「传感器表」的形式管理,每一项包含名称、是否启用、端口、引脚、ADC 通道与换算系数。目前表中仅「水位」一项处于启用状态,其余项以注释形式保留,便于后续扩展(如浊度、pH 等)。
4执行机构与驱动电路
4.1三路继电器驱动(排水泵 / 补水泵 / 加热模块)
排水泵、补水泵、加热模块三者都是「通电即工作」的大电流负载,采用继电器做电气隔离与开关控制。选用 SRD-05VDC-SL-C 型 5V 继电器,触点容量满足水泵与加热模块的需求。驱动电路为经典的三极管开关:
控制端:STM32 的 GPIO(PA3 排水泵、PA6 补水泵、PA7 加热模块)经 2kΩ 基极限流电阻接到 S8050 的基极。
开关端:S8050 的集电极接继电器线圈负极,线圈正极接 5V,发射极接地。GPIO 输出高电平时三极管饱和导通,继电器吸合。
续流保护:继电器线圈两端反向并联 1N4007。线圈断电瞬间会产生数倍于电源电压的反向电动势,若无续流二极管,这个尖峰很容易击穿三极管;并联后能量经二极管泄放,三极管得以保护。
软件约定:三路继电器均为「高电平吸合」,程序中统一用 Bit_SET / Bit_RESET 封装,避免各处直接操作寄存器。
表 1-2三路继电器分配
继电器 | 用途 | 控制引脚 | 驱动器件 | 保护 |
RLY5 | 排水泵 | PA3 | S8050 + 2kΩ 基极电阻 | 1N4007 续流二极管 |
RLY6 | 补水泵(进水泵) | PA6 | S8050 + 2kΩ 基极电阻 | 1N4007 续流二极管 |
RLY7 | 加热模块 | PA7 | S8050 + 2kΩ 基极电阻 | 1N4007 续流二极管 |
4.2TB6612FNG 电机驱动(喷淋臂)
喷淋臂由直流电机带动,需要正反转与调速能力。TB6612FNG 是双通道 H 桥驱动芯片,单通道持续电流 1.2A、峰值 3.2A,内置过热与低压保护,逻辑端 3.3V 可直接与 STM32 相连,无需电平转换。
表 1-3TB6612FNG 接线
TB6612FNG 引脚 | STM32 引脚 | 作用 |
AIN1 / AIN2 | PA4 / PA5 | 方向控制:一高一低正转,一低一高反转,双低滑行 |
PWMA | PA1(TIM2_CH2) | 速度控制:PWM 占空比 0~100% |
STBY | 接高电平 | 待机控制,拉高使能 |
VM / VCC / GND | 5V / 3.3V / GND | 电机电源 / 逻辑电源 / 地 |
AO1 / AO2 | 直流电机 | 电机输出 |
PWM 参数计算:系统时钟 72MHz,定时器 TIM2 预分频设为 36,得到 2MHz 计数时钟,再设自动重装值为 100,则 PWM 频率 = 72MHz / 36 / 100 = 20kHz。20kHz 已超出人耳听觉范围上限,电机不会发出啸叫;同时占空比的分辨率为 1%,正好对应 0~100% 的整数百分比调节,软件上用五档(0/25/50/75/100)即可满足洗涤需求。
4.3风扇、消毒灯与蜂鸣器
散热风扇(PA2):由 S8550 PNP 三极管驱动,低电平开启。风扇在风干阶段与加热阶段强制开启,前者用于吹干内胆,后者用于散热。
5W 紫外消毒灯(PB1):经 CN3 端子接入,由开关管控制,低电平点亮,消毒阶段点亮 30 秒。
有源蜂鸣器(PA8):低电平触发。因为有源蜂鸣器内部自带振荡电路,只需给直流电平即可发声,软件上用简单的延时开关实现不同节奏的提示音(按键短鸣、完成长鸣、故障连鸣)。
5人机接口
5.1OLED 显示
显示采用 0.96 英寸 SSD1306 驱动的 128×64 单色 OLED,通过软件 I2C 与主控通信:PB8 作 SCL、PB9 作 SDA。选择软件 I2C 而非硬件 I2C,是因为 SSD1306 的时序要求宽松,软件模拟更易调试,也不占用硬件 I2C 外设。
字库:8×16 ASCII 字库用于数字与英文字母,16×16 汉字字库用于中文提示,另有 8×6 小字库用于参数行。
显存模型:SSD1306 内部为 8 页 × 128 列的显存,每页 8 行像素,一次写入 8 位对应一列的 8 个像素。
寻址方式:本工程采用页内寻址绘制——绘制汉字时分别向第 (行−1)×2 页与第 (行−1)×2+1 页写入上下半个字模,列坐标则按「列号 × 16」计算;绘制 ASCII 时列坐标按「列号 × 8」计算。这样做的代价是同一行内汉字与 ASCII、以及相邻两行之间在显存上会互相覆盖,这也是实际屏幕上出现字符叠加观感的原因(详见《用户操作说明书》中的实机画面)。
刷新策略:只刷新发生变化的区域——时间区每秒刷一次,模式名与状态区在切换时刷新,避免整屏重绘造成闪烁。
5.2按键输入
4 个轻触按键(TS665CJ)分别接 PB3、PB4、PB5、PB6,采用「外部中断下降沿触发 + 定时器 20ms 消抖」的方案。
为什么用中断而不是轮询:洗涤流程中主循环需要维持状态机的节奏,若用轮询会因主循环周期不确定而漏键。
消抖:按键按下时引脚电平会抖动 5~15ms,程序在下降沿中断中置标志,再由 TIM1 的 20ms 周期任务读取并确认电平,确认稳定后才生成键值,既避免了抖动误触发,也不阻塞主循环。
键值映射:按键表按「端口、引脚、键码」三元组定义。需要注意的是,本板因布线关系,键码与引脚并非顺序对应(K1→PB3、K2→PB5、K3→PB6、K4→PB4),程序在表中显式定义,业务层只认键码,不受影响。
表 1-4按键表定义
按键 | 端口 | 引脚 | 键码 | 有效电平 |
K1 | GPIOB | PB3 | 1 | 低电平(按下接地) |
K2 | GPIOB | PB5 | 3 | 低电平 |
K3 | GPIOB | PB6 | 4 | 低电平 |
K4 | GPIOB | PB4 | 2 | 低电平 |
6WiFi 通信电路
联网采用 ESP-01S 模块(ESP8266 内核,8MB Flash),出厂烧录机智云 GAgent 固件。模块通过 2×4 排针插座(U37)插装在板上,与主控以串口相连,波特率 9600bps。
接线:模块 TX → PB11(USART3_RX),模块 RX → PB10(USART3_TX),另接 3.3V 与 GND。
为什么用 USART3:USART1 通常用于调试打印,USART2 引脚与其他功能复用,USART3 的 PB10/PB11 在本板上无冲突。
电平注意:ESP-01S 为 3.3V 器件,不能用 5V 直供;模块启动瞬间电流可达 200mA 以上,因此供电端需保证足够的去耦容量。
协议:主控侧在 gizwits_product.c 中把本地变量映射为机智云数据点,由协议层负责打包/解包,业务代码不直接处理协议帧。
7元器件清单概览
整套物料共 32 类、58 件(含备件),按功能可分为主控类、显示与交互类、传感器类、驱动类、执行器类、结构件与接插件类。下表列出主要器件,完整清单见配套的《洗碗机元器件清单汇总.xlsx》。
表 1-5主要元器件(概览)
类别 | 主要器件 | 数量 |
主控 | STM32F103C8T6 最小系统板(U47) | 1 |
显示与交互 | 0.96″ OLED(U2)、TS665CJ 轻触按键、有源蜂鸣器 BUZZER1、红色 LED | 1 / 4 / 1 / 4 |
传感 | DS18B20 水温传感器、电阻式水位传感器(U50) | 1 / 1 |
通信 | ESP-01S WiFi 模块(U37) | 1 |
驱动 | SRD-05VDC-SL-C 继电器 ×3、S8050 ×3、1N4007 ×3、2kΩ ×3、TB6612FNG(U14) | 多 |
执行器 | 排水泵、补水泵、加热模块、直流电机(U28)、5W 紫外消毒灯(CN3)、散热风扇(CN2) | 各 1 |
电源与接插 | Type-C(USB6)、自锁开关(U40)、1000μF(C3)、100nF(C1)、接线端子、XH 端子 | 多 |
结构 | CR2032 电池座(BT2)、M2 螺丝 ×4 | 1 / 4 |
第二部分软件介绍
1软件总体架构
软件基于 STM32F10x 标准外设库开发,采用「前后台」结构:中断服务程序负责时间基准、按键捕获与串口收发,主循环负责业务调度。代码按职责分为四层,上层只依赖下层的接口,不直接触碰寄存器。
表 2-1软件分层结构
层次 | 主要文件 | 职责 |
应用层 | main.c、config.c | 模式调度、洗涤流程状态机、阈值管理、定时任务、显示内容组织 |
设备抽象层 | config.c 中的设备表 / 按键表 / 传感器表 / 阈值表 / 定时任务表 | 用「表驱动」统一管理 7 路设备、4 个按键、2 路传感器、3 项阈值参数 |
板级驱动层 | OLED.c、Key.c、AD.c、DS18B20.c、DCMotor.c、Buzzer.c、MyRTC.c、GPIO_OUT.c、GPIO_IN.c | 封装各外设的初始化与读写 |
协议层 | gizwits_product.c、gizwits_protocol.c | 机智云数据点映射、协议打包与解析、网络对时 |
表驱动的设计是本工程的一个关键取舍。以设备表为例,7 路执行器被抽象为同一张表,每项记录名称、端口、引脚、有效电平与开关标志,于是「打开加热器」「关闭排水泵」这类操作可以写成同一个函数,传入设备索引即可,避免了 7 段几乎相同的开关代码。按键表、阈值表同理。日后新增一路设备,只需在表中加一行,业务代码无需改动。
表 2-2表驱动数据结构
表 | 内容 | 规模 |
设备表 devices[] | 消毒灯 PB1、加热器 PA7、补水泵 PA6、排水泵 PA3、风扇 PA2、电机、蜂鸣器 PA8 | 7 项 |
按键表 keys[] | (GPIOB, PB3, 键码 1)、(GPIOB, PB5, 键码 3)、(GPIOB, PB6, 键码 4)、(GPIOB, PB4, 键码 2) | 4 项 |
传感器表 sensorParams[] | 水位(启用)、温度(保留) | 2 项 |
阈值表 thresholdParams[] | S_W 水位阈值、WDB 温度阈值、Stp 步长 | 3 项 |
菜单表 menuItems[][] | 每个模式最多 4 个选项的菜单文本 | 按模式 |
定时任务表 timerTasks[] | 周期任务与回调 | 按需 |
2程序启动与主循环
2.1初始化顺序
初始化顺序经过刻意安排:先建立系统节拍(NVIC 分组与延时函数),再初始化依赖节拍的模块(RTC、蜂鸣器),然后是显示与输入(OLED、按键),接着是数据采集(ADC、DS18B20),最后是输出(GPIO_OUT、电机)。这样即使某个外设初始化失败,也能通过 OLED 或蜂鸣器给出提示。
代码 2-1系统初始化(System_Init)
void System_Init(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);delay_init();/* SysTick 延时基准 */BoardLed_Init();MyRTC_Init();/* RTC + 后备电池 */Buzzer_Init();OLED_Init();/* 软件 I2C:PB8=SCL, PB9=SDA */Key_Init();/* EXTI 下降沿 + TIM1 20ms 消抖 */Ad_Init();/* ADC1_CH0 → PA0 水位 */GPIO_OUT_Init();/* 继电器/风扇/消毒灯等输出 */GPIO_IN_Init();DS18B20_Init();/* 单总线 → PB0 */DCMotor_Init();/* TIM2_CH2 PWM → PA1 */ThresholdParams_LoadDefaults();/* S_W=50, WDB=55, Stp=5 */MY_Gizwits_Init();/* 机智云协议初始化 */} |
需要说明的是 BOOT_SHOW_PERINF 宏被设为 0:上电时跳过开机信息页,直接进入模式菜单。这样调试时省去每次 2 秒的等待,也避免了开机页在显存中残留影响后续页面。
2.2主循环
主循环不做任何阻塞等待,四个任务按顺序「跑一遍就走」:
代码 2-2主循环(main)
int main(void){System_Init();while (1){userHandle();/* 采集、状态机推进、显示刷新 */gizwitsHandle();/* 云端数据上报与命令解析 */gizwitsNTPTask();/* 网络对时(低频) */Key_GetNum();/* 读取消抖后的键值 */}} |
这里有一个容易踩的坑:如果把洗涤流程写成「开泵 → delay(30000) → 关泵」,那么这 30 秒内按键、显示、云端通信全部停摆,用户按 K4 也停不下来,超温保护也无法及时响应。因此本工程所有流程等待都采用非阻塞计时——只记录「开始时刻」,每轮循环比较已运行时间,到点才切换状态。这就是主循环必须保持「跑一遍就走」的原因。
3洗涤流程状态机(程序设计核心)
3.1为什么用状态机
洗碗是一个典型的「多阶段、带反馈、有超时」的时序过程:每个阶段都有明确的进入动作、退出条件和超时保护,而且阶段之间有依赖(不先进水不能加热,不排水不能风干)。用状态机描述这类过程有三个好处:
结构清晰:每个状态只关心「我现在做什么、什么条件走」,不用写成层层嵌套的 if。
安全可控:任何阶段都能被外部事件(按 K4、超温、缺水)立即打断,并统一走「安全退出」路径。
易于调整:阶段的时间参数全部宏定义,改节奏只改宏,不改逻辑。
3.2状态划分
表 2-3洗涤流程状态划分
状态 | 进入动作 | 退出条件 | 超时保护 |
IDLE 待机 | 全部执行器关闭 | 收到启动命令 | — |
DRAIN 排水 | 开排水泵 | 水位降到下限 或 到时 | DRAIN_TIMEOUT 30s |
FILL 进水 | 开补水泵 | 水位达到阈值 或 到时 | FILL_TIMEOUT 30s |
PREWASH 预洗 | 开电机低速 | 到时 | PREWASH 20s |
HEAT 加热 | 开加热器 + 风扇 | 水温到设定值 或 超时 | HEAT_TIMEOUT 100s |
MAIN_WASH 主洗 | 开电机 + 维持加热 | 到时 | MAIN_WASH 30s |
RINSE 漂洗 | 开电机 + 换水 | 到时 | RINSE 30s |
DRY 风干 | 关加热,开风扇 | 到时 | DRY 120s |
DISINFECT 消毒 | 点亮紫外灯 | 到时 | DISINFECT 30s |
DONE 完成 | 蜂鸣 3 秒 | 到时回到 IDLE | BUZZER 3s |
FAULT 故障 | 关闭所有输出、连鸣 | 用户确认复位 | — |
3.3状态迁移的实现
状态机每轮循环只做三件事:判断当前状态的退出条件是否满足、记录进入下一状态的时刻、执行新状态的进入动作。下面是一段体现核心思路的示意代码(工程中对应 DishwasherAutoState 的 switch 分支):
代码 2-3状态机的非阻塞推进
static void Dishwasher_Run(void){uint32_t elapsed = Now_ms() - stateEnterMs;/* 已运行时间 */switch (autoState){case FILL:Pump_Fill_On();if (WaterLevel >= ShuiWeiYu || elapsed >= FILL_TIMEOUT)State_Enter(HEAT);/* 水位到位或超时 → 加热 */break;case HEAT:Heater_On();Fan_On();if (TempX10 >= TempDs18b20Yu * 10)State_Enter(MAIN_WASH);/* 温度到位 → 主洗 */else if (elapsed >= HEAT_TIMEOUT)State_Enter(FAULT);/* 加热超时 → 故障 */else if (TempX10 >= OVER_TEMP_X10)/* 75.0℃ 超温保护 */State_Enter(FAULT);break;case MAIN_WASH:Motor_SetSpeed(SPEED_MID);if (elapsed >= MAIN_WASH_TIME)State_Enter(RINSE);break;/* … 其余状态同构 … */}} |
这段代码里最值得注意的两点:第一,没有任何 delay 或 while 等待,函数执行时间是微秒级,因此可以被高频调用;第二,所有退出条件都带「或超时」的兜底——传感器坏了、水管堵了,流程不会永远卡死,而是走故障路径。这是家用电器控制软件的基本要求。
3.4时间参数与可调性
表 2-4流程时间参数(宏定义)
宏定义 | 值 | 含义 | 调整场景 |
DRAIN_MIN | 5s | 排水最短时间 | 排水管路长时调大,确保残留水排净 |
DRAIN_TIMEOUT | 30s | 排水最长时间 | 防止水位传感器失效时无限排水 |
FILL_TIMEOUT | 30s | 进水最长时间 | 防止进水管断水时无限进水 |
PREWASH_TIME | 20s | 预洗时长 | 餐具较脏时调大到 30~40s |
HEAT_TIMEOUT | 100s | 加热最长时间 | 冬季水温低时可适当加大 |
MAIN_WASH_TIME | 30s | 主洗时长 | 演示时调小,实用时调到 3~5 分钟 |
RINSE_TIME | 30s | 漂洗时长 | 同上 |
DRY_TIME | 120s | 风干时长 | 环境湿度大时加长 |
DISINFECT_TIME | 30s | 消毒时长 | 按消毒灯功率调整 |
BUZZER_TIME | 3s | 完成提示音时长 | 按使用习惯调整 |
RTC_MAX_STEP | 5s | RTC 单次最大修正量 | 防止网络对时跳变导致流程计时异常 |
OVER_TEMP_X10 | 750 | 超温门限(75.0℃) | 安全门限,不建议修改 |
EMPTY_LEVEL | 5 | 判定无水的下限 | 水槽结构变化时可微调 |
4模式调度与菜单系统
系统通过两个变量共同决定界面:SystemMode 记录当前所处的工作模式,M_Switch 记录当前显示的是「菜单」还是「功能页」。这样的双变量设计让「进入」与「返回」变得非常简单——返回时只需把 M_Switch 置 1,不必保存和恢复页面内部的临时状态。
代码 2-4工作模式枚举(SystemMode)
typedef enum {MODE_YUANCHENG = 0,/* 远程模式 */MODE_AUTO,/* 自动模式 */MODE_MANUAL,/* 手动模式 */MODE_DINGSHI,/* 定时模式 */MODE_THRESHOLD,/* 阈值设置 */MODE_TIME_SET,/* 时间设置 */MODE_COUNT,/* 模式总数(用于取模循环) */MODE_USER,/* 用户模式 */MODE_YUYIN/* 语音模式 */} SystemMode; |
菜单项以二维数组 menuItems[MODE_TOTAL][4] 组织,每一项最多 4 个选项的显示文本。翻页时用「索引 + 1 再对 MODE_COUNT 取模」实现首尾循环,避免边界判断。屏幕上同时可见三项,选中项前显示「>>」,这也是用户判断当前选中位置的唯一依据。
5阈值参数管理
可在线调整的参数被抽象为统一的阈值参数表,每项包含名称、当前值、默认值、最小值、最大值与在屏幕上的显示位置。这样「在阈值设置页循环切换参数」「加减并限幅」都只需一次通用实现,不必为每个参数单独写代码。
代码 2-5阈值参数表
typedef struct {const char *name;/* 显示名,如 "S_W" */int16_t*value;/* 指向实际变量的指针 */int16_tdef;/* 默认值 */int16_tmin;/* 下限 */int16_tmax;/* 上限 */uint8_trow;/* 屏幕行 */uint8_tcol;/* 屏幕列 */} ThresholdParam;static ThresholdParam thresholdParams[] = {{ "S_W", &ShuiWeiYu,50, 0, 100, 2, 3 },/* 水位阈值 */{ "WDB", &TempDs18b20Yu,55, 0, 75,2, 4 },/* 温度阈值 */{ "Stp", &Step,5, 1, 10,2, 5 },/* 调节步长 */}; |
限幅逻辑在写入处统一处理:数值超出 [min, max] 时钳位到边界并给出短鸣提示。这一步很重要——如果没有限幅,用户把温度阈值误调到 200℃ 后,加热器会一直加热到超温保护才停,而超温保护只是最后一道防线,不应被频繁依赖。
6OLED 显示设计
显示内容按「固定区 + 变化区」划分:顶部一行固定显示 RTC 时间(每秒刷一次),第二行显示模式名与当前环节,第三行显示水位与温度等实时值,最下方显示设备开关符号。这种分区让刷新量降到最低。
本工程 OLED 采用页内寻址:绘制一个 16×16 汉字时,先向第 (行−1)×2 页写入字模的前 16 字节(上半字),再向第 (行−1)×2+1 页写入后 16 字节(下半字),列坐标按「(列−1)×16」计算;绘制 8×16 ASCII 时列坐标按「(列−1)×8」计算。由于页的位置由行号唯一决定,同一行内汉字与 ASCII 的列基准不同、相邻行又共用相邻页,实际屏幕上就会出现字符相互覆盖的观感。这是该驱动方案的真实特性,交付资料中的 26 张屏幕画面均按此寻址方式还原,与实机一致。
(a)自动模式待机页 |
(b)阈值设置页 |
图 2-1屏幕显示效果(按真实寻址方式还原,与实机一致)
7按键处理
按键的可靠性直接影响使用体验。本工程采用「下降沿中断 + 定时器消抖 + 键值队列」的三段式处理:
第一段(中断):EXTI 检测到下降沿后只做一件事——置位对应按键的标志位,不做任何延时。
第二段(定时器):TIM1 每 20ms 进入一次,读取标志后再采样一次引脚电平;两次电平一致才认为按键有效,并生成键值放入队列。20ms 大于轻触按键的典型抖动时间(5~15ms)。
第三段(主循环):Key_GetNum() 从队列取键值返回给业务层,业务层只关心「按了哪个键」,不关心消抖细节。
另外还做了「松开检测」:只有检测到按键松开后才允许产生下一个键值,避免长按被识别为连续多次按键。这也意味着本机的操作全部是点按,符合家电的使用习惯。
8物联网通信设计
8.1数据流
上行:主控采集到的水温、水位、流程状态被填入机智云数据结构体,由协议层打包成协议帧,经 USART3 发给 ESP-01S,模块再通过 WiFi 上传到云端,App 拉取后显示。下行则相反:App 下发的开关命令由模块收到后转发给主控,协议层解析出具体数据点,主控据此改变本地设备状态或阈值。
8.2数据点映射
本工程共定义 16 个数据点。其中有两点值得注意:一是温度上报使用「×10 的整数」而非浮点,避免不同平台浮点精度差异导致显示抖动;二是设备开关全部用布尔量,与本地设备表的开关标志一一对应,因此在 App 上点开关与在本地手动模式下点开关,走的是同一段执行代码,行为完全一致。
表 2-5数据点与本地变量的映射
数据点 | 类型 | 读写 | 映射的本地变量 / 功能 |
TempDs18b20 | 数值 | 只读 | 水温 ×10 |
ShuiWei | 数值 | 只读 | 水位相对值 0~100 |
flag_RE_BEN | 布尔 | 可写 | 加热器(设备表索引) |
flag_BU_SHUI_BEN | 布尔 | 可写 | 补水泵 |
flag_PAI_SHUI_BEN | 布尔 | 可写 | 排水泵 |
flag_DCMotor | 布尔 | 可写 | 直流电机 |
flag_FENG_SHAN | 布尔 | 可写 | 散热风扇 |
flag_Buzzer | 布尔 | 可写 | 蜂鸣器 |
flag_LED | 布尔 | 可写 | 紫外消毒灯 |
TempDs18b20Yu | 数值 | 可读写 | 温度阈值 TempDs18b20Yu |
ShuiWeiYu | 数值 | 可读写 | 水位阈值 ShuiWeiYu |
ModeSwitch | 布尔 | 可写 | 设备总开关 |
flag_DishWasher_State | 枚举 | 只读 | 洗涤流程当前状态 |
XiTong_ModeSwitch | 枚举 | 可写 | 系统工作模式 |
DishWasher_ModeSwitch | 枚举 | 可写 | 洗涤程序(预洗/主洗/漂洗/风干/消毒) |
8.3网络对时
ESP-01S 联网后可从云端获取标准时间。主控在 gizwitsNTPTask() 中把云端时间写入 RTC。为防止对时造成时间跳变影响流程计时,代码中设置了单次最大修正量 RTC_MAX_STEP = 5 秒:若新旧时间差超过 5 秒,则分多次逐步修正。这样既保证了长期走时准确,又不会让正在计时的洗涤流程突然「跳秒」。
9安全与异常处理设计
表 2-6异常处理策略
异常 | 检测方式 | 系统响应 | 用户可见反馈 |
水温超限 | TempX10 ≥ 750(75.0℃) | 立即断加热器、开风扇,进入 FAULT | 屏幕显示「故障」,蜂鸣器连鸣 |
加热超时 | HEAT 状态超过 100s 未达温 | 断加热,进入 FAULT | 同上 |
缺水 | WaterLevel < EMPTY_LEVEL | 禁止开加热器与电机,先补水 | 水位显示偏低,加热器不动作 |
进水超时 | FILL 状态超过 30s 未到位 | 关补水泵,进入 FAULT | 显示故障 |
排水超时 | DRAIN 状态超过 30s 未到位 | 关排水泵,进入 FAULT | 显示故障 |
WiFi 断线 | 协议层心跳超时 | 本地模式继续运行,仅停止上报 | App 显示离线,屏幕正常 |
按键抖动 | 20ms 二次采样 | 丢弃无效边沿 | 无异常 |
10调试经验与设计复盘
显存覆盖问题:最初以为是字库提取错误,逐一比对字模后才发现是页内寻址导致的行间覆盖。这提醒我们:显示异常的根因可能不在字库,而在寻址模型。修复方式有两种,本项目选择保留原驱动行为并在文档中如实说明,因为在 128×64 的屏上,行间距调整会连带影响所有页面的排版。
按键映射错位:键码与引脚不是顺序对应,排查时曾误以为是硬件虚焊。最终通过逐键打印键码确认了映射关系,并在按键表中显式写明,避免后来者再踩坑。
非阻塞改造:早期版本用 delay 实现阶段等待,结果按键无响应、超温保护失效。改为计时比较后问题全部消失,这也成为本项目最重要的一条设计约束。
继电器误动作:最初未加续流二极管,继电器断开瞬间偶发复位。补上 1N4007 后彻底解决。这类问题在实验室常被忽略,但在实际使用中会反复出现。
时间跳变:开启网络对时后,流程计时偶尔提前结束。加入 RTC_MAX_STEP 限幅后恢复正常。



















































































