ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于STM32与迪文屏的智能家居控制面板设计与实现

基于STM32与迪文屏的智能家居控制面板设计与实现 今年给客户做一套智能家居环境控制面板需求其实不复杂实时显示房间温湿度能手动设定目标温湿度再根据设定值自动控制加热器、加湿器和排风扇。刚开始我一度想用STM32裸驱一块彩色LCD把UI、触摸、字体全自己搞定结果做到一半就发现状态切换、菜单跳转这些工作远比想象中费时间。换用迪文屏DGUS之后界面开发直接从“写代码”变成了“画界面”STM32这边只需要管串口收发和控制逻辑整个项目节奏完全不一样了。这篇文章就把从选型到落地的完整过程写出来包括DGUS界面怎么搭、STM32怎么和屏通信、联动控制逻辑怎么写以及调试过程中踩过的坑给正在做智能家居项目、毕业设计或者想快速给产品配一套人机界面的同学做个参考。1. 为什么是迪文屏DGUS一次选型复盘1.1 项目要解决的核心问题先说我手里这个需求。控制面板必须做的事情有四件实时显示温度和湿度并且带小数位允许用户直接设定目标温度一键切换自动/手动模式显示加热器、加湿器、排风扇各自的当前运行状态。这些功能单独拎出来都不难但凑到一起对UI的要求就上来了。既要多个数字实时刷新又要有触摸交互还得表达设备状态。用OLED滚动显示信息量不够用LCD自己写菜单又得维护一大套状态机。这才是这个项目真正的难点所在——它不是算力问题而是人机交互的开发效率问题。1.2 三种人机交互方案的对比我先后评估过三条路线各有各的适用场景。第一OLED或者LCD1602。这俩优点是便宜缺点是信息量太小只能显示数字和简单符号触摸更不用想用户交互只能靠物理按键。如果面板只是放一两个温度数字这条路完全够用。第二STM32直接驱动彩色LCD再移植LVGL或者自己写UI。这条路线自由度最高但工作量也最大。字体、图标、触摸校准、菜单状态机全都要自己搞STM32F103这种小内存芯片跑LVGL会比较吃力图片资源还得压缩处理一旦做到多页面切换代码量能翻好几倍。第三串口屏方案其中迪文屏DGUS是性价比比较高的选择。屏幕端的UI、图片、字库、控件逻辑全部在PC端软件里完成生成一个DWIN_SET文件夹下载到屏上就生效。STM32这边只需要往指定的变量地址写数据、读取用户按键值不需要关心屏幕坐标、字体、图片这些事。三条路线我最终选了第三条核心原因是开发成本和视觉效果的平衡。客户要的是“看起来像产品”不是“看起来像开发板”。迪文屏可以直接导入现成的素材做界面MCU里完全不需要存图片数据这对STM32F103这种资源紧张的平台特别友好。1.3 什么项目适合这套方案得说清楚不是所有项目都该无脑上串口屏。如果界面只有一两个数字、几个按键OLED加物理按键反而更简单、更省成本。但如果界面信息量大、需要触摸设置参数、需要多页面切换迪文屏的价值就很明显了。智能家居控制面板、环境监测终端、工业小HMI这类场景都很匹配。把界面开发从单片机工程里剥离开本质上是在把开发精力放回真正的业务逻辑上。毕竟控制策略、传感器采集、设备联动才是这类项目的核心UI只是壳。2. 系统总架构与硬件选型STM32、SHT30、继电器和电源2.1 数据流链路整个系统的数据流用一句话就能说清SHT30采集温湿度STM32换算成物理值通过串口写到迪文屏的变量地址屏幕控件自动刷新用户触摸面板设置目标值屏把键值或输入值通过串口发给STM32STM32执行控制逻辑再把执行状态写回屏上。这个“变量地址”机制是整个DGUS的命根子后面单独讲。硬件上最关键的就是把这条链路上的每一环选对否则后面全是坑。2.2 主控选型STM32F103C8T6就够了主控我用了STM32F103C8T6。原因很直接这个项目不需要跑复杂算法不需要很大内存外设需求就是一路串口、一路I2C、一路PWM加上几个GPIOC8T6资源完全够用而且资料多、成本低、无论是CubeMX还是标准库都顺手。用F407甚至F103RCT6也可以逻辑完全一样。CubeMX配置要点RCC选择外部晶振SYS里的Debug选择Serial Wire不然第二次烧录会报找不到芯片USART1115200-8-N-1接迪文屏I2C1标准模式100kHz接SHT30若干GPIO输出接继电器和MOSTIM2_CH1输出PWM控制排风扇。这里有个细节要提醒Debug配置很多人会漏。如果选了No DebugKeil下载两次之后就提示连接不上芯片只能把BOOT0拉高擦除整个Flash再拉回来非常折腾。新建工程时直接选Serial Wire能省掉这一整段麻烦。2.3 温湿度传感器SHT30 还是 DHT22温湿度传感器我直接选了SHT30。不推荐DHT11也不推荐DHT22原因很现实。DHT11精度太差温度±2℃湿度±5%RH只能算“有数据”不能算“准确数据”做控制面板根本没法看。DHT22精度稍好温度±0.5℃湿度±2%RH但它走的是单总线协议时序要求非常严格。主机需要精确到微秒级的拉低、释放、采样在HAL库环境下稍不注意就被中断打断读出来的数据全错。网上大量“DHT22读取失败”的问题根因基本都是这个。SHT30是I2C接口带CRC校验精度±0.3℃读取逻辑简单可靠。唯一的缺点就是价格比DHT系列高几块钱但这点差价相比后期排查通信问题省下来的时间太值了。SHT30读取代码片段static uint8_t sht30_crc(uint8_t *data, uint16_t len) { uint8_t crc 0xFF; while (len--) { crc ^ *data; for (uint8_t bit 0; bit 8; bit) { crc (crc 0x80) ? (crc 1) ^ 0x31 : (crc 1); } } return crc; } void sht30_read(float *temp, float *hum) { uint8_t cmd[2] {0x2C, 0x06}; uint8_t buf[6]; HAL_I2C_Master_Transmit(hi2c1, 0x44 1, cmd, 2, 100); HAL_Delay(50); HAL_I2C_Master_Receive(hi2c1, 0x44 1, buf, 6, 100); if (sht30_crc(buf, 2) ! buf[2] || sht30_crc(buf 3, 2) ! buf[5]) { return; } uint16_t tr (buf[0] 8) | buf[1]; uint16_t hr (buf[3] 8) | buf[4]; *temp -45.0f 175.0f * tr / 65535.0f; *hum 100.0f * hr / 65535.0f; }CRC校验千万别省。I2C总线在继电器动作、电源波动时容易产生干扰如果读到错数据还拿去控制继电器后果很严重。SHT30的数据帧正好自带CRC校验通过才使用不通过就丢一次采样反正采集周期很快。2.4 执行器件和电源设计执行器件分三路加热器一路继电器控制220V加热设备加湿器一路继电器控制加湿器排风扇NMOS驱动12V风扇MCU输出PWM调速。继电器一定要选带光耦隔离的模块同时在线圈两端加续流二极管。继电器线圈是感性负载断电瞬间会产生反向电动势如果不加吸收打坏单片机引脚是常有的事。PWM频率建议设在10kHz到25kHz之间。太低了风扇会啸叫那声音和蚊子叫差不多晚上放卧室客户肯定投诉。我实际用20kHz听不到噪音驱动也稳。电源部分整个系统用12V适配器输入12V直接给风扇5V给继电器模块3.3V给STM32和传感器。迪文屏的供电电压要看具体型号有的是3.3V有的是5V。系统统一共地屏和主板之间必须共地否则串口的电平参考点都不一样数据必乱。3. DGUS工程搭建从一张底图到可交互面板3.1 先理解变量地址机制DGUS最核心的概念是VP变量地址。屏幕上的数值、开关、图标这类控件在DGUS软件里都会绑定一个16位变量地址比如0x1000。MCU往这个地址写数据屏幕上的控件就会自动更新显示用户触摸屏幕上的输入控件屏幕也会把结果写到指定地址。这个思路非常像PLC里的寄存器。好处就是MCU完全不用关心屏幕在哪个坐标画了什么东西只需要往指定地址读写数据就行UI和业务彻底解耦。我自己的工程地址规划如下地址内容数据方向0x1000实时温度0.1℃为单位MCU → 屏0x1001实时湿度0.1%为单位MCU → 屏0x2000模式切换按键返回值屏 → MCU0x3000目标温度设定值0.1℃为单位屏 → MCU0x4000设备状态位图标MCU → 屏这个表是整个工程的地图。实际项目里地址可以随意规划但强烈建议在Excel里建一张地址分配表避免前后控件叠加覆盖。DGUS界面里如果两个控件绑了同一个地址显示会互相干扰这种问题在PC端预览时看不出来只能靠地址表排查。3.2 素材准备与控件布局界面素材我用Photoshop做了一张800x480的背景底图分辨率必须和屏幕一致。底图上把固定的标题、文字、边框全部画好动态内容留空然后在DGUS软件里叠加动态控件。具体控件配置实时温度放一个“数据变量显示”控件变量地址0x1000数据类型选有符号整数整数位数2小数位数1单位填“℃”。DGUS里所有变量本质都是整数不支持浮点所以小数必须自己放大10倍再传输。23.4℃在协议里发234屏上显示23.4。如果你把浮点数直接发过去那就干脆显示乱了。目标温度用“数据变量输入”控件绑定0x3000同样放大10倍。用户点击这个区域屏会弹出数字键盘输入结果自动写进0x3000STM32周期读取这个地址即可。模式切换用“按键值返回”控件绑0x2000键值填1勾选“自动上传”。这样用户按下时屏幕会自动给MCU发一帧数据MCU不需要主动查询。状态图标用“位图标”控件绑定0x4000分别配置bit0、bit1、bit2对应加热器、加湿器、排风扇的开和关两种图标。布局上我习惯把温度湿度放在屏幕中上部一眼能看到目标温度和模式按钮放在中下部符合操作习惯状态图标放在底部不抢视觉焦点。3.3 生成DWIN_SET并下载到屏DGUS软件里工程编辑完成后点击生成所有资源会被打包成一个DWIN_SET文件夹。把这个文件夹整个拷贝到TF卡根目录插到屏背面的卡槽重新上电屏幕会自动烧录烧完有进度提示。等界面变成新工程断电拔卡就完成了。这里有三个坑必须注意TF卡必须是FAT32格式NTFS不识别DWIN_SET里的文件名不要改改一个字母都可能下载失败下载完成后最好把卡拔掉或清空否则下次上电还会重复烧录浪费时间。3.4 三个容易忽略的设置第一个是波特率。DGUS工程的串口波特率默认常见是115200但不同固件版本可能有差异最好在软件里确认。波特率不一致的表现很典型STM32发指令屏完全没反应但屏自身显示正常。用串口助手先发一帧验证要比直接接单片调快得多。第二个是触摸校准。部分屏出厂时触摸没有校准点击位置偏得离谱。CFG配置里有一项触摸校准开启后下载屏会进入校准界面点击十字校准点完成后再关闭该项下载一次免得每次开机都进校准。第三个是数据变量输入控件需要配套的键盘资源。有些DGUS版本里输入控件自带的键盘库不全没有键盘底图和GUI配置SD卡下载时会提示缺文件。开发前先打开示例工程把键盘相关文件一并放到DWIN_SET里。4. STM32与迪文屏的通信落地0x82/0x83指令与驱动代码4.1 最小指令集DGUS串口协议帧格式很简洁帧头5A A5然后是长度、指令、参数。写变量用0x82指令格式如下5A A5 [Len] 82 [Addr_H] [Addr_L] [Data_H] [Data_L]Len表示从指令字节开始到帧尾的字节数。写一个16位数据Len就是05。读变量用0x83指令5A A5 04 83 [Addr_H] [Addr_L] 01最后的01表示读取一个字16位。屏收到后回复5A A5 06 83 [Addr_H] [Addr_L] 01 [Data_H] [Data_L]注意所有数据都是大端格式高字节在前。这个点我踩过坑有一段时间把所有数据高低字节写反结果26.5℃显示成91.0℃排查了半天才发现是字节序问题。4.2 发送驱动向屏写入实时温湿度STM32侧写一个串口发送函数就够了void dgus_write_word(uint16_t vp, uint16_t value) { uint8_t frame[8]; frame[0] 0x5A; frame[1] 0xA5; frame[2] 0x05; frame[3] 0x82; frame[4] vp 8; frame[5] vp 0xFF; frame[6] value 8; frame[7] value 0xFF; HAL_UART_Transmit(huart1, frame, 8, 50); }实际使用时温度计算完要立即放大10倍并转换成整数。负温度要处理成补码比如-5.0℃对应-50强转成uint16_t之后是0xFFCE直接发过去屏上有符号显示控件会自动显示成-5.0。主循环里每500ms刷一次sht30_read(temp, hum); uint16_t temp_x10 (uint16_t)(int16_t)(temp * 10.0f); uint16_t hum_x10 (uint16_t)(hum * 10.0f); dgus_write_word(0x1000, temp_x10); dgus_write_word(0x1001, hum_x10);刷新周期不用太短屏幕控件本身刷新不过来500ms肉眼完全够用还能降低串口负载。4.3 接收解析读取目标温度和按键值目标温度的读取我配置的是屏上按钮按下后自动上报而不是MCU周期性发0x83去查。前者响应快实现也简单。按键值返回的上报帧不同固件略有差异典型的格式是5A A5 [Len] 81 [VP_H] [VP_L] [键值_H] [键值_L]具体字节序以你手上屏对应的《开发指南》为准。MCU端接收我用一个最简单的状态机串口中断收字节进数组主循环里按帧头找、按长度截帧。void uart_rx_handler(uint8_t byte) { static uint8_t rx_buf[16]; static uint8_t idx 0; rx_buf[idx] byte; if (idx 2 !(rx_buf[0] 0x5A rx_buf[1] 0xA5)) { idx 0; } if (idx 3 idx (rx_buf[2] 3)) { parse_dgus_frame(rx_buf, idx); idx 0; } }逻辑很直白前两字节不是5A A5就清空重来收满“长度字段3”个字节当作一帧处理。这种固定短帧协议用这个状态机足够不用上复杂的环形缓冲区。parse_dgus_frame里面根据指令类型分流0x81按键上报看键值切换自动/手动模式0x83读变量回包取目标温度数据存进全局变量set_temp_x10。4.4 通信链路自检新板子第一次调试别急着写完整逻辑。先用USB转TTL模块把屏接到电脑串口助手手动发一帧5A A5 05 82 10 00 00 64如果0x1000绑定的温度控件显示100也就是10.0℃说明屏和协议都没问题。如果没反应先检查波特率再检查屏的接口是TTL还是RS232。很多串口屏背面同时有TTL排针和RS232插座接错就完全不通。确认屏端正常后再接STM32。把STM32的TX接到屏的RX发一帧心跳帧看屏是否响应。这样一层一层剥离能快速定位是电平问题、波特率问题还是代码问题。5. 联动控制逻辑从“屏上显示”到“自动化控制”5.1 目标温度下发与滞回控制用户通过屏设置目标温度后0x3000里就是用户输入值放大10倍的整数。STM32周期读取这个地址得到目标值set_temp_x10。温度控制我用滞回区间而不是等号直接比较。比如目标22.0℃实际温度低于21.0℃打开加热器高于23.0℃关闭加热器。这种设计主要防止继电器在目标值附近频繁吸合。继电器机械寿命和触点火花都在这个设计下得到明显改善。float set_temp set_temp_x10 / 10.0f; if (!heater_state temp set_temp - 1.0f) { heater_state 1; HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_SET); } else if (heater_state temp set_temp 1.0f) { heater_state 0; HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_RESET); }滞回宽度设1℃比较合适。太窄继电器依然会频繁动作太宽温度波动会很明显。5.2 湿度联动与PWM风扇调速除湿部分我用排风扇处理。湿度超过设定值加10%时开启排风扇低于设定值则关闭。同时温度控制里如果温度过高排风扇也可以按比例调速把风量跟温差做线性映射uint16_t duty 0; if (temp set_temp 1.0f) { float diff temp - (set_temp 1.0f); if (diff 3.0f) diff 3.0f; duty (uint16_t)(diff / 3.0f * 800.0f); } __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty);PWM频率设20kHz占空比按0到1000算。这里有个细节排风扇和加热器要加互斥逻辑。加热器开启期间排风扇只保留基础换气占空比不能一边疯狂加热一边猛烈排风否则系统会陷入震荡温度永远控制不住。5.3 状态回显让屏实时反映控制结果控制状态必须实时回显到屏上让用户看到自动控制到底在做什么。前面在DGUS里设计了0x4000作为设备状态位图标地址STM32每200ms拼一次状态字写过去uint16_t status 0; if (heater_state) status | 0x01; if (humidifier_state) status | 0x02; if (fan_on) status | 0x04; dgus_write_word(0x4000, status);这样屏上的加热器、加湿器、排风扇图标会跟着实际IO状态切换实现“面板所见即设备所得”。做产品时这个细节很重要用户能直观看到系统在响应自己的操作使用感受完全不一样。5.4 控制逻辑的抗干扰处理我的处理方式有三条传感器连续两次CRC失败就保留上次数据不更新显示避免屏上数字乱跳继电器状态改变后加50ms延时等IO稳定再回传状态串口读取目标温度加超时保护连续3秒通信异常就自动切回手动模式并关闭所有执行器保证安全。这些不是花架子。智能家居设备如果通信卡死执行器停在开启状态轻则浪费电重则出安全事故。通信异常即停止执行是这类设备控制逻辑的底线。6. 调试与量产前的避坑清单6.1 屏上显示0或者满量程现象是温度区域显示0.0或者直接显示整屏最大值。排查思路有两个方向。先用串口助手往那个地址发固定值比如发0x03E8也就是1000。如果显示0说明地址或者控件类型没对上如果显示100.0说明MCU发送链路有问题。然后再从MCU侧打印实际发送的帧看长度和字节序。我自己遇到过把帧长度字段写错的屏端直接丢弃整帧看起来就像“屏幕没收到数据”。另外有个不常见但很坑的数据变量显示控件的位数配置不够。比如整数位设了2位结果数值超过99屏上会显示0或者最高位丢失。数值范围和显示位数一定要在开发时留足余量不然客户把温度设到30℃屏上直接变0那场景极度尴尬。6.2 触摸没反应或者配置没生效先别怀疑屏坏了多数情况是这几类工程没有真正下载成功。SD卡不是FAT32或者文件没有放进DWIN_SET文件夹按键值返回控件里没勾“自动上传”导致屏不主动发数据MCU串口解析状态机被坏帧卡死。我最初那版状态机在连续噪声下一直清零重来后来改成“最多8字节没有找到有效帧头就强制复位”问题就消失了。还有一个容易被忽略的有些屏烧录完成后必须重新断电再上电触摸和串口配置才生效。热插拔SD卡不算一次完整上电很多人在这里反复折腾。6.3 通信不稳定与静电干扰继电器开关的瞬间单片机偶尔死机或者串口数据乱码根源通常是电源地线太细以及继电器模块没有光耦隔离。继电器线圈通断产生反向电动势通过地环路干扰串口信号这是最常见的干扰路径。解决方案继电器模块选带光耦的控制板铺地继电器供电和单片机供电之间做好单点连接或者加磁珠屏和主板之间的串口线尽量短双绞线更好如果产品要过EMC测试串口线上加TVS管。这些措施看起来基础实际量产中全是血泪教训。室内智能家居环境还好如果面板靠近大功率设备干扰会非常明显。6.4 踩完这些坑之后我的建议如果让我重新做一遍我会在硬件上直接把迪文屏的供电和主板分开只共地避免屏的背光电流波动影响单片机的供电稳定性。软件上我会把DGUS地址规划表做成一个头文件所有地址用宏定义而不是散落在代码各个角落后期维护和排查会舒服很多。控制策略先跑纯软件仿真把滞回参数调好再接真实执行器安全又高效。最后分享一个提升调试效率的小技巧迪文屏和STM32联调时在串口线上挂一个逻辑分析仪比在MCU里printf加日志更省事。逻辑分析仪能看到每一帧实际从串口线上传输的电平和数据用来判断是哪一方没发、帧内容对不对、字节序是否正确基本一眼就能定位问题。设备已经跑起来的场合这个技巧更是救命。
返回列表