简介:面向电子、自动化及嵌入式课程设计与毕业设计场景的“单片机温度控制风扇”完整方案文档,针对传统电风扇风速不能随温度自动调节、机械定时噪声大且范围有限、缺少远程遥控等痛点展开。压缩包内为1个doc文档,约167KB,按目录组织总体硬件设计、DS18B20温度传感器处理、电机调速与控制、温度显示与控制、软件流程及主要程序等章节,并配有系统原理框图和电路设计思路。内容以AT89C51单片机为核心,涉及5档风速、睡眠循环、12小时以内定时开关机、温度每变化3℃自动升降档、红外遥控、数码管显示及RS232串口上位机控制等功能。已有7262人学习下载,适合搭建温度控制风扇、撰写设计文档或对照软硬件实现思路的读者参考与二次开发。
1. 一块 51 板子做出温度控制风扇:先说清这套东西的边界
这份《温度控制风扇(完整)》的工程资料,摊开来是一套以 AT89C51 为核心的单片机温度控制风扇系统:DS18B20 采环境温度、数码管显示、键盘设档、双向可控硅调电机转速,外带红外遥控、12 小时定时和 RS232 串口上位机接口。它不是那种"跑个流水灯就交差"的课设,五档风速、每 3℃ 自动升降一档、睡眠间歇运转、过零触发调速这几块都落到了具体电路和汇编代码上。适合谁?手里有 51 开发板、想做一件能真正插上电机转起来的东西、又不想从零画板子的电子/自动化方向学生和刚转嵌入式的工程师。不适合想要现成 PCB 文件直接下单打样的人——资料的核心价值在"控制逻辑 + 时序代码",硬件部分得自己照原理图落地。
2. 硬件选型拆解:AT89C51、DS18B20 与双向可控硅怎么搭
这套方案能不能跑通,70% 取决于三件事:主控的 IO 够不够且时序稳、温度传感器是不是数字量、电机侧能不能安全地和单片机隔离。下面按信号链从核心往外拆。
2.1 主控为什么是 AT89C51 而不是更便宜的方案
原稿的选型理由写得很直白:AT89C51 内部已含定时器、程序存储器、数据存储器,不需要外接存储器和定时器件,能直接构成最小系统。放到今天看,这话依然成立——4KB Flash 装这套程序绰绰有余(主程序 + DS18B20 三个子程序 + 中断服务 + 显示扫描,编译出来通常不到 2KB),128B RAM 里划出 20H~21H 存温度、5AH~66H 存档位和计数余量也够。
但有一个地方必须纠正,否则你照着接线图去打样会翻车:原稿写"具有 4KB 片内 E2PROM 的 AT89C51",这个描述不准确。AT89C51 内部是 4KB Flash 程序存储器,掉电保持的是程序,不是数据。你想让风扇记住"上次关机时是 3 档"或者"定时 8 小时"这类参数,Flash 是存不住的——它没有字节级擦写能力,反复写还会磨损。常见做法是外挂一片 I2C 的 24C02(2Kbit,256 字节),占 P3.5/P3.6 两根线,几十行汇编就能读写。原稿没做这一步,所以它的定时和档位断电即失,这属于设计取舍,不算 bug,但你复现时要知道这是有意的简化。
| 资源 | 容量 | 这套系统里的用途 |
|---|---|---|
| Flash | 4KB | 主程序、DS18B20 时序、中断服务、显示扫描 |
| RAM | 128B | 20H/21H 存温度原始值,5AH~66H 存档位与过零计数 |
| 定时器 | 2 个 | T0 做显示动态扫描与红外解码计时,T1 备用 |
| 中断源 | 5 个 | INT0 接过零检测,INT1 接红外接收头 |
| IO 口 | 32 根 | P3.7 走单总线,P1 段码,P2 位码/键盘 |
还要注意 P3.7 这根线的复用身份:它第二功能是 /RD,外部数据存储器读选通。系统只要不外扩 RAM、不执行MOVX指令,把它当普通准双向 IO 用是安全的。一旦你后续想加片外 RAM 存历史温度曲线,P3.7 会在每次MOVX时被硬件自动拉低,单总线立刻被干扰。这种坑不看手册根本想不到,所以我一般建议单总线固定换到 P3.4 或 P1.7 这种没有总线复用的脚上。
2.2 DS18B20 的接法与 4.7k 上拉电阻的位置
原稿原理图里 DS18B20 三根线:VCC 接 5V、GND 接地、DQ 接 P3.7,DQ 上通过 R1(4k7)拉到 VCC。这是标准的外部供电接法,比寄生供电稳得多。
上拉电阻这个 4.7k 不是随便取的。单总线在空闲态靠上拉保持高电平,主机和从机都是开漏/准双向输出,只能拉低不能推高。线短(<1m)时 4.7k 足够;线拉到 3~5 米,线容加上分布参数会让上升沿变缓,读时隙采样点容易误判,这时候要把上拉降到 2.2k 甚至 1k。原稿的板子是板载传感器,4.7k 完全合适,别自作聪明改。
另外原稿图上画了 R2~R8 一排 4k7 挂在 P2 口上——那是 8 位数码管的位选限流或上拉,不是单总线的东西,看原理图时别把两处 4k7 搞混。
2.3 过零检测、光耦、双向可控硅:一条必须隔离的驱动链
电机侧是整个系统里唯一碰 220V 的部分,这一段的顺序是:市电 → 过零检测电路 → 单片机 INT0 → 单片机 IO → 反相器 4009 → 光耦 MOC3041/MOC3031 → 双向可控硅 → 电机。
MOC3041 是过零触发型光耦,内部集成了过零检测,只有当市电电压过零附近(通常 ±20V 以内)时才允许输出侧导通,其余时间锁死。这个特性带来两个后果,必须想清楚:
第一,它天然抑制了开通瞬间的浪涌电流和 EMI,对电机这种感性负载很友好,也不会在电网上制造高频干扰。
第二,它做不了相位控制。相位控制要求在正弦波的任意角度精确开通,靠调节导通角 α 改变输出电压有效值——MOC3041 只能在过零点开,你想在 23.5° 开它根本不理你。
原稿在 2.3.1 里花了大量篇幅算控制角 α(0°、23.5°、46.5°、61.5°、76.5°)和对应的触发延时 t(0、1.70、2.58、3.43、4.30ms),但 3.2.1 的程序设计原理写的却是"改变每个控制周期内可控硅导通和关断交流完整全波信号的个数"——这是周波控制,不是相位控制。两套思路在文档里并排出现,实际能落地的是后者。这个不一致我在第 4 章会展开讲怎么取舍。
硬件上还有两个细节:光耦输出侧要并在可控硅的 A1-A2 之间,A2 侧串 300~500Ω 限流电阻(原稿画的是 10k,有点大,实际取值建议按可控硅的 IGT 换算,常见 330Ω);可控硅选 BT136/BT137 这类 4A/600V 以上的,配一个小散热片。感性负载还要加 RC 吸收网络(100Ω + 0.1μF),否则关断瞬间的 dv/dt 可能让它自己重新导通。
2.4 显示与键盘:HD7279A 和 MAX7221 不是同一颗芯片
这是我看这份资料时第一个皱眉的地方。原稿 2.4 节文字写"通过 HD7279A 控制芯片组建一个单片机键盘输入与显示模块,其中包括一个 2*8 的键盘矩阵和 8 段动态扫描数码管",但紧接着的原理图里,器件位号标的是 U1 = MAX7221,DIG0~DIG7 加 8 个 200Ω 限流电阻,这是 MAX7219/MAX7221 的典型接法。
这两颗芯片功能重叠但引脚和协议完全不同:
| 对比项 | HD7279A | MAX7221 |
|---|---|---|
| 接口 | 串行,CS/CLK/DATA 三线 + 一根 KEY | SPI 三线(DIN/CLK/LOAD) |
| 键盘扫描 | 支持,最大 8×8 矩阵 | 不支持 |
| 数码管驱动 | 8 位,带译码 | 8 位,带译码 |
| 亮度调节 | 软件指令 | 外接 ISET 电阻 + 软件 |
| 键值读取 | 有独立 KEY 引脚中断 | 无 |
也就是说,原稿需求里那个 2×8 键盘矩阵,MAX7221 根本做不了。你在复现时必须在两条路里选一条:要么用 HD7279A,串行口 + KEY 引脚,键盘和显示一块芯片全包,接线最少;要么保留 MAX7221 做显示,键盘单独用 P1 口扫 2×8 矩阵,多占 10 根 IO。
原稿的按键功能(风速 5 档、定时加减、睡眠开关、模式切换)大概需要 8~10 个键,2×8 矩阵刚好够。我一般会走第一条:HD7279A 省 IO,代价是它比 MAX7221 贵一些,而且时序要求严格(CLK 高电平宽度不能小于 2μs),12MHz 主频下直接置位/清零就能满足,不用插延时。
3. DS18B20 时序落地:从 12 位原始值到实际温度
DS18B20 是单总线器件,全部通信靠一根线的高低电平时间长短来编码,没有时钟线。这意味着所有时序都得靠软件延时凑,指令周期算错一点就整条总线失联。这一章把原稿的汇编拆开算一遍。
3.1 温度寄存器的数据格式与符号位处理
DS18B20 上电默认 12 位分辨率,温度值存在暂存器第 0、1 字节,低字节在前。12 位里低 4 位是小数(分辨率 0.0625℃),高 12 位是补码形式的整数部分。
原稿给了一张对照表,我把几个关键值拎出来,配合计算过程说明:
| 实际温度 | 二进制输出 | 十六进制 |
|---|---|---|
| +85℃(上电默认) | 0000 0101 0101 0000 | 0550H |
| +25.625℃ | 0000 0001 1001 0001 | 0191H |
| +10.125℃ | 0000 0000 1010 0010 | 00A2H |
| +0.5℃ | 0000 0000 0000 1000 | 0008H |
| 0℃ | 0000 0000 0000 0000 | 0000H |
| -0.5℃ | 1111 1111 1111 1000 | FFF8H |
| -10.125℃ | 1111 1111 0110 1110 | FF5EH |
| -55℃ | 1111 1100 1001 0000 | FC90H |
验证一下 +25.625℃:25.625 / 0.0625 = 410 = 0x019A。等一下,0x0191 = 401,401 × 0.0625 = 25.0625,不是 25.625。原稿这张表有两处对不上——这是照抄器件手册时抄串行了,你看手册原文会发现 +25.0625℃ 对应 0191H。同理 -25.625℃ 原稿写 FF6FH,实际 -25.625/0.0625 = -410,补码是 FE66H。
这个细节不影响代码,但影响你调试时的判断:如果你按表去核对读数,会发现永远差半度多,然后怀疑程序。我的建议是别信任何抄来的对照表,直接用两条公式自己算:
- 正温度:
T = raw × 0.0625 - 负温度:
T = ( (~raw + 1) & 0x0FFF ) × (-0.0625)
其中 raw 是 16 位原始值。负温度那步先取反加一再去掉高 4 位符号位,不能直接乘。
3.2 初始化、写、读三个时序的汇编实现
原稿用的是 12MHz 晶振,1 个机器周期 = 12 / 12MHz = 1μs,所以DJNZ指令(2 个机器周期)正好 2μs,可以直接拿来做延时标尺。这一点是整个时序能对上手册的前提,换 11.0592MHz 晶振所有延时都得重算。
先看复位(初始化)子程序:
;=========== DS18B20 复位 =========== ; P3.7 为单总线,12MHz 下 DJNZ 约 2us RST_DS18B20: SETB P3.7 ; 总线释放为高,准备拉低 NOP CLR P3.7 ; 拉低总线,产生复位脉冲 MOV R1,#3 RST1: MOV R0,#110 DJNZ R0,$ ; 内层约 220us DJNZ R1,RST1 ; 三层合计约 660us,满足 >480us SETB P3.7 ; 释放总线,等待器件应答 NOP NOP MOV R0,#25H RST2: JNB P3.7,RST3 ; 15~60us 内器件会把总线拉低 DJNZ R0,RST2 ; 约 74us 超时窗口 LJMP RST4 ; 超时未应答,判为器件不在 RST3: SETB FLAG ; 收到应答,置存在标志 LJMP RST5 RST4: CLR FLAG ; 无应答,清标志,后续要报错 LJMP RST7 RST5: MOV R0,#115 RST6: DJNZ R0,$ ; 约 230us,等应答低电平结束 RST7: SETB P3.7 RET参数说明:拉低时间 660μs 落在手册要求的 480~960μs 区间中段,留了余量;应答窗口 74μs 比手册的 60μs 稍宽,是为了容忍长线传输的延迟。FLAG这个位是全局的"器件存在"标志,主程序发现它为 0 就别再去读温度了,直接数码管显示Err。
再看写子程序,低位先出:
;=========== 向 DS18B20 写一个字节 =========== ; 入口:A 中为待写数据 WR_DS18B20: MOV R2,#8 ; 8 位 CLR C WR1: CLR P3.7 ; 拉低,开始写时隙 MOV R3,#6 DJNZ R3,$ ; 低电平保持约 14us(写 1 需 1~15us) RRC A ; 带进位右移,先出最低位 MOV P3.7,C ; 写 0 继续拉低,写 1 释放为高 MOV R3,#25 DJNZ R3,$ ; 时隙总长补到约 64us SETB P3.7 ; 每个时隙结束都释放总线 NOP DJNZ R2,WR1 SETB P3.7 RET这里的技巧在RRC A:DS18B20 是低位先出,而 51 的RRC刚好把 bit0 挤进 CY 再写进 IO,省掉了移位判断的分支。写 0 时 IO 拉低持续 64μs(满足 ≥60μs),写 1 时 14μs 后释放,剩余 50μs 保持高电平,两种时隙都能被器件正确识别。
读子程序:
;=========== 从 DS18B20 读一个字节 =========== ; 出口:A 中为读到的数据;R1 指向接收缓冲区 RD_DS18B20: MOV R4,#2 ; 连读 2 字节(温度低字节 + 高字节) MOV R1,#20H ; 缓冲区起始地址 20H READ1: MOV R2,#8 READ2: CLR C SETB P3.7 NOP NOP CLR P3.7 ; 拉低 >1us,启动读时隙 NOP NOP NOP SETB P3.7 ; 立即释放,把总线交给器件驱动 MOV R3,#9 READ3: DJNZ R3,$ ; 约 18us 后采样 MOV C,P3.7 ; 读回一位到 CY MOV R3,#23 READ4: DJNZ R3,$ ; 补足约 60us 时隙长度 RRC A ; 拼进累加器 DJNZ R2,READ2 MOV @R1,A INC R1 DJNZ R4,READ1 RET采样点落在拉低后约 23μs 处,比器件手册建议的 15μs 晚了 8μs。板载、短线、5V 供电的情况下能稳定读到,但如果你的传感器引线拉到 2 米以上,或者用了 3.3V 供电(DS18B20 在 3.3V 下内部电荷泵建立慢),这个采样点就偏危险了。血泪经验:把MOV R3,#9改成MOV R3,#6(约 12μs 采样),再把后面的#23改成#26补回时隙长度,长线场景下误码率会明显下降。
3.3 主程序怎么把原始值变成档位
原稿的主循环只有短短几行,但信息密度很高:
;=========== 主程序:周期采集 + 显示 =========== MAIN: LCALL RST_DS18B20 ; 复位,同时探测器件 LCALL GET_TEMPER ; 启动转换并读回 2 字节 MOV A,20H ; 低字节 MOV C,08H ; 21H 的 bit0 RRC A ; 带进位右移,高字节符号位补进来 MOV C,09H ; 21H 的 bit1 RRC A MOV C,10H ; 22H 的 bit0 RRC A MOV C,11H ; 22H 的 bit1 RRC A MOV 20H,A ; 结果存回,等效于 16 位值右移 4 位 LCALL DISPLAY ; 刷新数码管 AJMP MAIN这段的逻辑是把 12 位原始值的低 4 位小数丢掉,只留整数摄氏度——四次带进位右移合起来等于把 16 位值整体右移 4 位。注意 08H、09H 是 21H 字节的位地址,10H、11H 是 22H 的位地址,原稿这里其实借用了 22H 当临时高位,用之前没清零,如果 22H 里残留了别的数据,移进来的就是垃圾。稳妥做法是在GET_TEMPER返回后先MOV 22H,#0。
GET_TEMPER原稿没贴出来,按 DS18B20 的标准流程补齐如下:
;=========== 启动一次温度转换并读回 =========== GET_TEMPER: LCALL RST_DS18B20 MOV A,#0CCH ; SKIP ROM:总线上只有一片,跳过匹配 LCALL WR_DS18B20 MOV A,#44H ; CONVERT T:启动转换,12 位需 750ms LCALL WR_DS18B20 LCALL DELAY_750MS LCALL RST_DS18B20 MOV A,#0CCH LCALL WR_DS18B20 MOV A,#0BEH ; READ SCRATCHPAD:读 9 字节暂存器 LCALL WR_DS18B20 LCALL RD_DS18B20 ; 只取前 2 字节,即温度值 RET档位映射的逻辑原稿写在设计任务里:温度上升 3℃ 升一档,下降 3℃ 降一档。这条规则有个必须补的细节——迟滞。假设当前 3 档对应 26℃,温度在 26.0 附近抖动,读数一会儿 25.9 一会儿 26.1,档位就会在两档之间反复横跳,电机跟着一顿一顿地加减速,听着像坏了。我一般会做一个 1℃ 的死区:升档阈值用T ≥ 档位基准 + 3,降档阈值用T ≤ 档位基准 - 3 + 1,也就是降档要比升档多降 1℃ 才触发。原稿的结束语里自己承认"对于两个档之间的临界温度处理不好",说的就是这个。
3.4 12 位分辨率下的转换时间与程序结构
DS18B20 在 12 位模式下转换一次需要 750ms(手册标称最大 750ms,典型 200ms 左右但必须按最大值等)。原稿主循环是"转换—等待—读数—显示"串行执行,意味着数码管每 750ms 才刷新一次温度,看上去像卡住了。
常见做法是把转换和读取拆开:主循环每 20ms 刷一次显示(用 T0 中断做动态扫描),每 750ms 才走一次完整的转换-读取流程,用一个软件计数器标记。这样显示是流畅的,温度刷新慢一点用户也感知不到。
另一个问题是精度过剩。风扇控温根本不需要 0.0625℃ 的分辨率,把配置寄存器写成0x1F(9 位模式,0.5℃,93.75ms 转换)既省时间又省心。9 位模式下原始值右移 3 位就是整数,还能省掉那四次RRC。原稿没做这个配置,直接在默认的 12 位模式下跑,属于保守做法,能用但没必要。
4. 可控硅调速:过零中断计数法怎么算
电机调速这块是整份资料里最容易看晕的部分,因为原稿同时给出了两套互相矛盾的方案。这一章把公式拆开算,也说清楚为什么最后只能选其中一套。
4.1 相位控制与周波控制:硬件决定了你只能选一个
两种调速方式的本质区别在"什么时候开通可控硅":
相位控制:每个半波都开通,但开通时刻往后推一个控制角 α,输出电压的有效值随 α 增大而减小,电机连续可调。优点是调速平滑、无级;缺点是每次开通都在正弦波的中段,电压跳变率 du/dt 大,对电网产生大量谐波和射频干扰,感性负载还容易触发可控硅误开通。
周波控制(也叫过零控制、通断控制):可控硅只在电压过零点开通和关断,永远是完整的正弦波。通过改变"一个控制周期内导通了几个完整波、关断了几个完整波"来调节平均功率。优点是零干扰、零浪涌,对电网最友好;缺点是在低速档会出现肉眼可见的抖动,因为功率是断续的。
原稿的 2.3.1 节算了一堆控制角,那是相位控制的算法;2.3.2 节的电路用的是 MOC3041,那是过零型光耦,硬件上把相位控制这条路堵死了;3.2.1 节的程序原理写的是"改变每个控制周期内可控硅导通和关断交流完整全波信号的个数",这是周波控制。三处放在一起,只有周波控制能跑通。
我的判断:这份资料如果直接照 2.3.1 的表去写延时触发代码,接上 MOC3041 会发现完全没有反应——因为你算的 1.70ms 延时触发根本没机会执行,光耦在过零点自己就导通了。这不是代码问题,是方案冲突。
4.2 五档转速与线速度核算
原稿的风叶线速度公式是V = π × D × n / 1000,其中 V 单位 m/min,D 是扇叶最大扫出圆直径(mm),n 是转速(r/min)。国家标准要求最高档线速度不超过 2150 m/min。
代入一台 400mm(16 寸)的台扇:V = 3.14 × 400 × 1250 / 1000 = 1570 m/min,远低于 2150,安全。反推上限:n ≤ 2150 × 1000 / (3.14 × 400) = 1711 r/min,手册算出的 1555 就是这批数据下的上限,取整到 1250 留了 20% 余量。
原稿给出的五档转速是 1250 / 1150 / 1063 / 980 / 875 r/min,相邻档位大约差 8%~10%,这个梯度在体感上是能分辨的,而且最低档 875 仍有足够风量。
| 档位 | 转速 r/min | 控制角 α | 触发延时 t | 每周期导通波数(50Hz,1s 周期) |
|---|---|---|---|---|
| 5(最高) | 1250 | 0° | 0 ms | 50 |
| 4 | 1150 | 23.5° | 1.70 ms | 40 |
| 3 | 1063 | 46.5° | 2.58 ms | 30 |
| 2 | 980 | 61.5° | 3.43 ms | 22 |
| 1(最低) | 875 | 76.5° | 4.30 ms | 15 |
右列是我按周波控制的思路补的:50Hz 市电 1 秒有 50 个完整波(100 个过零点),导通 50 个波就是全速,导 15 个波约等于 30% 功率。这个换算和左边的控制角没有数学等价关系,两列不能互相验证——这也是原稿混用两套方案的直接后果。实际调试时以转速表实测为准,每周期导通波数只是起始参考值。
4.3 INT0 过零中断服务程序的三层计数
过零检测电路的输出接单片机 INT0,市电每过一次零点产生一个下降沿。50Hz 下这就是每秒 100 次中断(正负半波各一次),中断服务程序必须极短,否则主程序没法干活。
原稿的中断服务程序用了三层递减计数,变量语义有点绕,我把它的结构还原如下:
;=========== 过零中断服务程序 =========== ; INT0 每个过零点触发一次(50Hz 下 100 次/秒) ORG 0003H INTD0: PUSH ACC PUSH PSW PUSH DPH PUSH DPL SETB 24H.0 ; 进入中断标志,主程序据此禁止改参数 MOV A,5FH ; 5FH:本周期剩余导通波数 JZ TING1 DEC A MOV 5FH,A JMP FAN2 TING1: ; 导通波数已耗尽,本波不触发,直接跳到周期计数 FAN2: MOV A,5BH ; 5BH:间歇运转时的"停顿"波数 JZ TING2 DEC A MOV 5BH,A JMP ZONG TING2: ; 停顿计数归零 ZONG: MOV A,59H ; 59H:一个控制周期的总波数(=100,即 1 秒) JZ KAI DEC A MOV 59H,A JMP EXIT KAI: CLR 24H.0 ; 周期结束,清中断标志,允许主程序改参数 JB 26H.0,KAI2 ; 26H.0:睡眠模式标志 KAI2: MOV A,66H ; 66H:下一周期要导通的波数 JZ EXIT1 EXIT1: MOV 5FH,5AH ; 5AH:当前档位对应的导通波数 MOV 5BH,66H ; 重新装载停顿计数 MOV 59H,#100 ; 重装周期长度 EXIT: POP DPL POP DPH POP PSW POP ACC RETI三个变量的分工是:59H 从 100 往下数,数到 0 表示 1 秒控制周期结束;5FH 决定这个周期里有多少个过零点要放行可控硅;5BH 是睡眠模式下"转一会儿停一会儿"里那个"停"的计数。合起来刚好能实现原稿设计任务里的第 1 条(睡眠控制:循环转一段时间停一段时间)。
原稿这段代码有两处需要修正才稳:一是JZ之后没有明确的触发输出动作,实际应该在这里根据标志决定是否给光耦驱动脚输出低电平(经 4009 反相后驱动光耦);二是在EXIT1标签前的逻辑是空的,MOV 5FH,5AH无论66H是否为零都会执行,导致睡眠模式的"停"永远不生效。复现时要把装载动作放进JZ的分支里。
4.4 功率公式与实际档位标定
原稿给的功率公式是P = (n / N) × U × I,其中 n 是给定时间内导通的正弦波个数,N 是同一时间内正弦波总数,U、I 是全导通时的电压电流有效值。这个公式成立的前提是负载纯阻性或近似阻性——风扇电机是感性负载,功率因数在 0.6~0.8 之间,实际功率会比公式算的低一些。
验证一下:假设某台风扇标称 60W,5 档全导通 n/N = 1,P ≈ 60W;3 档取 n/N = 30/50 = 0.6,P ≈ 36W;1 档 n/N = 15/50 = 0.3,P ≈ 18W。但实测往往只有 15W 左右,差的这部分就是功率因数滞后和无功分量。所以调速档位的实测标定不能只靠算,得用钳形表量电流或者干脆用转速表量转速,反推每档该导几个波。
我一般会做一个"标定模式":上电时长按某个键 3 秒进入,此时 5 档轮流跑 10 秒,每档结束时把实测转速记下来填进表里,跑完一圈退出。这样换一台电机不用改代码重烧,只改 RAM 里的一张表就行。原稿没有这个功能,属于加分项,但对要做多台样机的场景很实用。
5. 避坑与排查:这套系统跑不通时的五个现场
纸上推演都对,焊上板子不转,这才是常态。下面五条是这类项目里出现频率最高的问题,按我踩过的顺序排。
5.1 现象:上电第一次读温度永远是 85℃
原因:DS18B20 上电后暂存器里的温度寄存器被复位成 +85℃,这是器件出厂默认值,不是环境温度。如果主程序在第一次CONVERT T完成之前就去读暂存器,读到的必然是 0550H。很多人的程序里DELAY_750MS写成了DELAY_75MS,少等了一个数量级,每次都读到 85。
解决:三步确认。第一,把延时子程序单独拿出来,用示波器量一个 IO 口翻转,确认真的是 750ms 而不是 75ms。第二,CONVERT T之后不要死等,改成读两次,第一次丢弃,第二次才采用。第三,在温度值后面加一个有效标志位,只有完成过一次完整转换才置 1,标志为 0 时数码管显示--,避免上电瞬间那一秒显示 85 误导调试。
5.2 现象:数码管全亮或者乱码,按键也没反应
原因:三种可能。一是 HD7279A 和 MAX7221 混用,位号和实际焊的芯片对不上,这两颗芯片的指令集完全不同,发0x09在 MAX7221 里是"禁止译码",在 HD7279A 里可能是别的意思,出来的当然是乱码。二是 MAX7221 的 ISET 电阻没接或阻值不对,这颗芯片靠 ISET 引脚对地的电阻设定段电流,不接就是全黑或者极暗。三是 8 个位选 DIG0~DIG7 的顺序和软件里的位码表反了,看着像乱码其实是数字换位。
解决:先确认手头到底是哪颗芯片,对着数据手册的引脚图逐一核。MAX7221 的 ISET 用 10kΩ 时段电流约 40mA,太大了发烫,一般用 22kΩ~47kΩ。位序问题写个测试程序,固定只点亮第 0 位,从 0 到 7 逐个试,能对上就说明位码表对了。
5.3 现象:风扇要么全速要么完全不转,中间三档没区别
原因:过零中断根本没进来。INT0 是外部中断 0,要满足三个条件才响应:EA 置 1、EX0 置 1、IT0 设为边沿触发(SETB IT0)。很多人只开了 EA 和 EX0,忘了 IT0,默认是低电平触发,过零检测输出是一个窄脉冲,低电平持续时间不够,中断进不来,可控硅就一直保持上次的状态。
另一种可能是过零检测电路的输出幅度不够。光耦 PC817 加桥式整流的经典过零电路,输出端没有上拉电阻,单片机读到的是浮空电平,永远判不出下降沿。
解决:在中断服务程序第一行翻转一个空闲 IO 口,用示波器看这个口有没有 100Hz 方波。有,说明中断正常,问题在后面的触发逻辑;没有,回头查 IT0 和过零电路的上拉。我习惯在调试版里固定留一根"心跳脚",量一下就知道中断活没活。
5.4 现象:温度读数在 ±2℃ 之间来回跳,档位跟着乱切
原因:单总线被干扰。最常見的是传感器引线太长又没做屏蔽,和电机线捆在一起走;其次是上拉电阻用了 10k 以上,上升沿太缓;还有就是主循环里刷数码管的动态扫描和单总线读时序撞车,扫描中断打断了读时隙,采样点错位。
解决:先做物理隔离,传感器线单独走,远离电机和可控硅;上拉降到 4.7k 以下;软件上在读 DS18B20 期间关掉总中断(CLR EA),读完再开——单总线一次读一个字节只要 60μs×8,关中断不到 1ms,对数码管扫描没影响。再加上第 3 章说的 1℃ 迟滞死区,观感会好很多。
5.5 现象:定时功能设了 8 小时,实际 6 小时多点就关了
原因:定时基准算错了。原稿用 T0 做定时,如果按 50ms 中断一次、计 20 次为 1 秒来算,初值应该是65536 - 50000 = 15536 = 0x3CB0。但如果在中断服务里做了别的事情(比如顺带扫了一位数码管),中断响应延迟累积起来,实际周期会变成 50.2ms 甚至 50.5ms,一小时就差几十秒,8 小时累计偏差能到十几分钟。
解决:定时基准不要靠软件循环凑,直接用 T0 工作在模式 2(8 位自动重装),或者模式 1 手动重装时把重装值写在中断最前面,减少其他代码的影响。更稳的做法是用 32.768kHz 的时钟芯片(DS1302 之类)做真实时间基准,单片机只管读,不自己数——这套资料没上时钟芯片,属于可以扩展的点。
6. 联调与验证:把每一步都钉死再往下走
这套系统有 220V、有单总线、有串行显示、有过零中断,四条链路任何一条没通都会表现成"不工作",所以联调绝对不能一把梭全焊上再上电。我自己的顺序是分四步,每步都有明确的合格判据。
第一步,只焊最小系统和显示。AT89C51 + 晶振 + 复位 + 数码管驱动,程序里只跑一个从 0 到 9 循环的计数。判据:数码管稳定显示、无闪烁、无重影。这一步过不了,后面全是白费。
第二步,接 DS18B20,只做测温不做控制。数码管显示整数温度,用手捏住传感器看数值往上走。判据:室温 25℃ 左右读数稳定在 ±0.5℃ 内,手指加热后 10 秒内上升 3℃ 以上。这一步能同时验证时序和上拉电阻。
第三步,接光耦和可控硅,但负载先不接电机,接一个 40W 白炽灯泡。灯泡的亮度变化肉眼可辨,比电机直观得多。判据:1 到 5 档灯泡从暗到亮分得出五级,且切换时不闪不炸。这一步专门验证过零中断和周波计数。
第四步,才接电机。判据:五档转速用转速表实测,和设计值偏差在 10% 以内;连续跑 30 分钟可控硅温度不超过 60℃。
串口这条链路单独验。原稿设计了 RS232 上位机控制,但没给协议。我一般定一套极简的 ASCII 协议,方便用串口助手直接敲:
| 上位机下发 | 含义 | 风扇回传 |
|---|---|---|
S3 | 切到 3 档 | OK,SPD=3 |
T+1 | 定时加 1 小时 | OK,TMR=1 |
T0 | 取消定时 | OK,TMR=0 |
R | 查询当前状态 | SPD=3,T=26C,TMR=1,SLEEP=0 |
M1/M0 | 开/关睡眠模式 | OK,SLEEP=1 |
对应的接收处理就是个状态机,收到\r判一条命令,首字符分支,后面解析数字。用 9600 波特率、T1 工作在模式 2 自动重装,TH1 = TL1 = 0xFD(12MHz 下 9600 的经典值,误差 0.16%,能接受)。这套协议的好处是拿任何一个串口助手都能调,不用写上位机软件。
最后一个技巧,也是我每次做完这类项目必做的一步:给所有"魔数"建一张表,写进代码开头的注释块里,包括定时器初值、每档导通波数、温度阈值、迟滞宽度。理由很实在——三个月后你自己回来看,绝对想不起MOV 5FH,#40里的 40 是哪来的。我当时做第一版的时候没记,后来要改一台 18 寸的扇子,光反推这个 40 花了一晚上,最后还是靠示波器一档一档数出来的。从那以后我每次动笔写控制代码,都先把参数表列出来再写逻辑,改参数只改表头那一处。希望帮到你。
本文还有配套的精品资源,点击获取