简介:一套面向51单片机入门与课程设计的直流电机控制方案,涵盖PWM加减速、启动/急停及高温报警功能,配合Proteus完成仿真,能够帮助电子类专业学生或爱好者快速掌握单片机定时器/PWM、按键处理与DS18B20温度检测的综合应用。资源包共43个文件,大小约954KB,主要包含C语言源程序、头文件、可烧录的hex文件、Proteus仿真工程、原理图文档、元件清单表格、流程图及若干效果截图,从代码到电路图均有覆盖。当前已有356人下载学习,适合作为课程设计、毕业设计或自学实践参考。资料中提供完整的源代码与仿真环境,附带了原理图、物料清单和可视化流程图,便于对照硬件连接和程序逻辑进行调试,也节省了自行绘制与整理的时间,尤其适合需要快速获得可运行原型的学习者。
1. 基于51单片机的直流电机项目,真正的工作量在“加减速”和“温度PWM”两个词里
打开 Proteus 仿真文件,想让直流电机转起来只需要一个电源和一根信号线,10 分钟就能看到旋转。但同一套题目里加上“加减速”和“温度PWM”,性质就变了:加减速不是给电机的电压跳变,而是 PWM 占空比按按键平滑增减;温度PWM则意味着 DS18B20 测到的温度偏差要反过来修正占空比,让电机转速既受手动控制,又被温度参数自动补偿。这是 51 单片机课程设计里最典型的闭环组合,把开环调速、单总线测温、Proteus 原理图和仿真图验收串成一条完整链路。下面按硬件选型、PWM 生成、温度闭环、仿真验收四条线展开,参数可以直接抄,边界也一并说明。
2. 硬件链路与器件选型:原理图里电流路径先于代码
硬件设计的顺序应该是:先定电流路径,再写配置代码。原理图上要能一眼看出三块回路——最小系统、PWM 电流放大级、温度采集输入级。把这条路径画清楚,后面写代码才有依据。
2.1 51单片机最小系统:晶振频率决定 PWM 时间基准
STC89C52RC 和 AT89C51 在 Proteus 里的仿真行为一致,都可以当主控。晶振选 12MHz 而不是 11.0592MHz,原因是机器周期正好是 1us,定时器初值可以直接心算:12MHz / 12 = 1MHz,一个机器周期 1us。若题目后续要加串口通信,那时再换 11.0592MHz 去凑标准波特率,纯做 PWM 调速用 12MHz 更顺手。
复位电路用 10uF 电解电容接 VCC、10k 电阻接地,RC 时间常数约 100ms,能保证上电复位可靠。这里有个容易漏的点:P0 口是开漏输出,如果原理图里接了两位共阴数码管显示温度和占空比,必须给 P0 加 10k 排阻上拉,否则段码电流不够,数码管亮度不均甚至不亮。Proteus 里不加上拉也能侥幸跑通,实物上会直接翻车。
2.2 电机驱动选型:Proteus 用 ULN2003,实物再考虑 L298N
ULN2003A 是达林顿管阵列,在 Proteus 元件库里直接搜 IN1 到 IN7 就能找到,不需要加载任何第三方模型,这是它在这个题目里的最大价值。接法也很固定:单片机 P1 口接 ULN2003 的输入脚,输出脚是集电极开路,直流电机一端接 VCC、另一端接输出脚。这样 P1 口只出毫安级控制电流,电机的大电流走外部 5V 电源,不会回头灌进单片机。
| 驱动方案 | 是否支持正反转 | Proteus 自带模型 | 实物成本 | 适用场景 |
|---|---|---|---|---|
| ULN2003A | 否 | 自带,直接可用 | 约 2 元 | 单向加减速,Proteus 仿真的默认选择 |
| L298N | 是 | 需额外加载库文件 | 约 15 元 | 实物要求正反转的完整 H 桥方案 |
| TB6612 | 是 | 需额外加载库文件 | 约 10 元 | 对 PWM 频率和噪声更敏感的小车 |
既然标题只写了加减速,没有提正反转,ULN2003 在成本和仿真通过率上都是最优解。L298N 的特点是 ENA 引脚要单独输入 PWM,ENB 控制另一路,Proteus 里若模型加载不对,会出现使能脚电平正确但电机不转的情况,答辩演示时很尴尬。选型上我给的建议是:仿真文件固定用 ULN2003,实物想换 L298N 时只需要改驱动板接线,代码不用动。
2.3 DS18B20 温度传感器:上拉电阻与寄生供电
DS18B20 是单总线器件,数据脚 DQ 必须接一个 4.7k 上拉电阻到 VCC。Proteus 仿真模型对这一点的宽容度比实物高很多,不加也能读出温度,但原理图作为设计文档必须把 4.7k 画上,否则交付时会被问“这个电阻为什么没有”。
接法上,DQ 接 P3.7,VCC 接 5V,GND 接地。需要注意的是 DS18B20 支持寄生供电模式,即 DQ 在特定时序里兼作电源,但课程设计里直接外部供电稳定性最好,不要为省一根线去用寄生模式,仿真和实物行为会有差异。上拉电阻的取值也不是随便定的,4.7k 在 5V 下提供约 1mA 上拉电流,满足单总线 15us 时隙的上升沿要求;换成 10k 也能工作,但线缆稍长时波形边沿会变缓,误码率上升。
3. 加减速的产生:定时器中断生成可调占空比 PWM
PWM 是这个项目的核心输出,也是答辩时最容易被抓着问细节的部分。原理上,直流电机转速正比于电枢两端平均电压,PWM 通过固定频率下改变高电平占比来改变平均电压。加减速的本质就是改变这个占比。
3.1 为什么 Proteus 仿真不用 STC89C52 的硬件 PCA
STC89C52 内部有 PCA 模块,配置 CCAPM0、CCAP0H 后可以硬件输出 PWM,不占 CPU。但 Proteus 自带的 AT89C51/AT89C52 仿真模型并不完整支持 STC 增强型寄存器,CCAPM0 这个地址在模型里没有对应行为,填写了也不会产生波形。所以 Proteus 仿真必须退回定时器软件模拟 PWM,这是这类项目里第一个隐性坑。实物烧录时,如果换成 STC89C52,可以再启用硬件 PCA 省出 CPU,但仿真文件和实物代码要保持一致,才能保证验收时两端行为可对照。
3.2 定时器0方式2:10us 中断与 100 级占空比
软件模拟 PWM 需要同时满足两个参数:PWM 频率和占空比级数。它们的关系是:定时器溢出周期 = PWM 周期 / 级数。以 1kHz PWM、100 级占空比为例,定时器必须每 10us 中断一次,12MHz 晶振下机器周期 1us,定时器0 方式2 初值 = 256 - 10 = 0xF6。这样电机的通电时间被切成 100 份,占空比步进 1%,加减速手感足够平滑。
| 参数 | 数值 | 计算依据 |
|---|---|---|
| 晶振频率 | 12MHz | 机器周期 1us |
| 定时器0工作方式 | 方式2,8位自动重装 | TH0 与 TL0 相同,无需重复赋值 |
| 定时初值 | 0xF6 | 256 - 10us = 246 = 0xF6 |
| PWM 级数 | 100 级 | counter 从 0 计到 99 |
| PWM 频率 | 约 1kHz | 10us x 100 = 1ms |
注意中断服务程序的现场保护会额外占用约 2-3us,实际 PWM 频率会落在 950-1000Hz 之间,Proteus 虚拟示波器测到的是实际值,不是理论值。若想提高 PWM 频率到 10kHz,每个级别只剩 1us,定时器初值 0xFF 没有余量,中断出入栈抖动会直接造成占空比不均匀,所以这个方案在 12MHz 晶振下做到 1kHz 就是合理边界。
3.3 按键加减速与占空比限幅逻辑
三个按键分别承担加速、减速、模式切换。加减速的粒度取 1%,长按连续加也是同样的逻辑,只是每 100ms 再执行一次。占空比不能到 0% 和 100%:0% 时电机停转,PWM 波形消失,示波器上无法判断系统是死机还是正常输出;100% 时电机满压运行,失去 PWM 调速意义,且 ULN2003 的功耗会明显上升。限幅范围取 5% 到 95%,这就是软件上的“死区保护”。
3.4 源代码:PWM 输出与按键调速最小实现
#include <reg52.h> sbit PWM_OUT = P1^0; // PWM输出,接ULN2003输入 sbit KEY_UP = P3^2; // 加速按键 sbit KEY_DOWN = P3^3; // 减速按键 unsigned char counter = 0; // PWM级数计数器 0-99 unsigned char pulse = 50; // 当前占空比 5-95 void Timer0_Init(void) { TMOD &= 0xF0; // 只改低4位,保留定时器1配置 TMOD |= 0x02; // 定时器0方式2,8位自动重装 TH0 = 0xF6; // 初值246,10us中断 TL0 = 0xF6; ET0 = 1; // 使能定时器0中断 TR0 = 1; // 启动定时器 EA = 1; // 开总中断 } void Timer0_ISR(void) interrupt 1 { counter++; if (counter >= 100) counter = 0; if (counter < pulse) PWM_OUT = 1; // 前pulse个时隙输出高 else PWM_OUT = 0; // 后续时隙输出低 } void Key_Scan(void) { static unsigned char key_delay = 0; if (!KEY_UP) { key_delay++; if (key_delay >= 10) { // 约100ms消抖 if (pulse < 95) pulse++; // 加速,限幅上限95 key_delay = 0; } } else if (!KEY_DOWN) { key_delay++; if (key_delay >= 10) { if (pulse > 5) pulse--; // 减速,限幅下限5 key_delay = 0; } } else { key_delay = 0; } } void main(void) { Timer0_Init(); while (1) { Key_Scan(); } }逻辑说明:定时器0每 10us 中断一次,counter 从 0 到 99 循环一周就是 1ms,即 1kHz PWM 周期。pulse 是阈值,counter 小于 pulse 时输出高,反之输出低。pulse=50 时高电平占 50 个时隙、低电平占 50 个时隙,占空比正好 50%。按键消抖没有用独立定时器,而是借用主循环的扫描间隔叠加计数,这种写法在 PWM 项目里足够稳定,也不会额外占用定时器资源。
参数说明:TMOD &= 0xF0 是为了不影响定时器1 的配置,如果代码里只用了定时器0,也可以直接写 TMOD = 0x02。pulse 的初始值 50 对应上电即半速,方便答辩演示时先看到转动再调速。限幅 5 和 95 必须写在加减逻辑里,不能让 pulse 越界后 counter < pulse 的比较产生异常波形。
4. 温度闭环:DS18B20 读数如何修正占空比
温度 PWM 的含义是把 DS18B20 测到的温度作为反馈量,温度升高时占空比自动增大,电机转速上升,带动风扇或散热结构降温。这是一个简单的比例控制回路:偏差越大,修正量越大,最后用限幅保证安全。
4.1 单总线时序为什么在 Proteus 里更容易踩坑
DS18B20 的复位脉冲、写时隙、读时隙都是微秒级时序,Proteus 仿真时 CPU 执行速度比实物快,延时函数经常出现“仿真能跑、实物挂了”的反向问题。更关键的是,PWM 定时器每 10us 中断一次,会打断 DS18B20 的读时隙采样。解决方法是读温度前关掉定时器0中断,读完再开:
sbit DQ = P3^7; // DS18B20数据引脚 void DS18B20_Reset(void) { DQ = 0; Delay_Us(480); // 480us复位脉冲 DQ = 1; Delay_Us(80); // 此处检测存在脉冲,简化处理 Delay_Us(400); } void Read_Temperature(void) { unsigned char low, high; ET0 = 0; // 关闭PWM中断,保护单总线时序 DS18B20_Reset(); // 跳过ROM、启动转换的写指令省略 DS18B20_Reset(); low = DQ_ReadByte(); // 温度低字节 high = DQ_ReadByte(); // 温度高字节 ET0 = 1; // 恢复PWM输出 // 温度拼接与符号处理 }逻辑说明:ET0=0 期间定时器0停止,PWM_OUT 保持最后电平,电机在温度采集的几毫秒内会短暂停转,但因为温度采集周期通常是 500ms 一次,这几毫秒不会引起肉眼可见的转速波动。如果温度读取频率提高,需要把采集放到 PWM 的低电平区间做,但课程设计里关中断是最可靠的方案。
4.2 温度偏差到占空比的线性映射
比例映射公式是:最终占空比 = 手动设定占空比 + (当前温度 - 设定温度) × Kp。Kp 取 2 时,温度每高 1 度,占空比增加 2%,到 95% 饱和。映射关系可以做成表格方便答辩讲解:
| 温度区间 | 修正动作 | Kp 作用 |
|---|---|---|
| 低于设定温度 3 度以上 | 不修正,按手动占空比输出 | 避免低温下电机全速转 |
| 高于设定温度 3 度以内 | 按偏差比例增加占空比 | 线性散热,转速平滑上升 |
| 高于设定温度 10 度以上 | 占空比饱和到 95% | 防止占空比越界损坏驱动管 |
这个映射本质是 P 控制器,没有积分项,所以存在稳态误差,但对风扇类负载足够。不要在这个题目里强行套完整 PID,单片机资源有限且答辩验收只看温度变化时占空比是否跟随,比例控制演示效果最直观。
4.3 温度读取与 PWM 联动代码
unsigned int temperature; // 当前温度,单位0.1℃ unsigned char manual_pulse; // 手动设定占空比 unsigned char final_pulse; // 最终输出占空比 #define SET_TEMP 250 // 设定温度25.0℃ #define KP 20 // 比例系数,单位0.1℃ void Temperature_Control(void) { int delta; temperature = Read_Temperature(); // 读取DS18B20 delta = (int)temperature - SET_TEMP; // 温度偏差 if (delta < 0) delta = 0; // 低温不修正 final_pulse = manual_pulse + (delta * KP) / 100; if (final_pulse > 95) final_pulse = 95; // 上限饱和 if (final_pulse < 5) final_pulse = 5; // 下限保护 pulse = final_pulse; // 更新定时器使用的占空比 }逻辑说明:SET_TEMP 用 250 表示 25.0℃,Read_Temperature 返回的温度也是 0.1℃ 单位,这样小数部分不会丢失。delta 的单位是 0.1℃,KP 设成 20 表示每 1℃ 偏差修正 2% 占空比。乘法在 51 上不会溢出:delta 最多几百,乘 20 后仍在 int 范围内。manual_pulse 是按键加减的结果,temperature 是反馈量,final_pulse 是合成后的实际输出,三个变量区分清楚,答辩时逻辑链就很顺。
4.4 Proteus 仿真接线与温度模型调整
仿真接线时,DS18B20 的 DQ 接 P3.7,VCC 接 5V,GND 接地,DQ 到 VCC 之间放一个 4.7k 电阻。Proteus 里想要演示温度变化,不要在原理图里直接改器件属性,运行时双击 DS18B20,在属性窗口的 Model Parameters 里调整 Temperature 或使用滑动条,仿真会自动刷新。此时观察电机转速:温度值调高,PWM 占空比上升,电机明显加速;调回低温,占空比回落到手动设定值。这个演示过程是整个答辩的核心证据。
5. 物料清单、流程图与示波器验收
5.1 一份可以直接按图采购的 BOM 表
| 序号 | 元件 | 规格参数 | 数量 | 备注 |
|---|---|---|---|---|
| 1 | 51单片机 | STC89C52RC 或 AT89C51 | 1 | Proteus 用 AT89C52 替代 |
| 2 | 晶振 | 12MHz | 1 | 机器周期 1us |
| 3 | 瓷片电容 | 30pF | 2 | 晶振负载电容 |
| 4 | 电解电容 | 10uF/25V | 1 | 复位电路 |
| 5 | 电阻 | 10kΩ | 1 | 复位下拉 |
| 6 | 电阻 | 4.7kΩ | 2 | DS18B20 上拉、备用 |
| 7 | 排阻 | 10kΩ × 8 | 1 | P0 口上拉 |
| 8 | 按键 | 6×6mm 轻触 | 3 | 加速、减速、模式 |
| 9 | 电机驱动 | ULN2003A | 1 | 达林顿管阵列 |
| 10 | 直流电机 | 3-6V 玩具电机 | 1 | 可带减速器 |
| 11 | 温度传感器 | DS18B20 TO-92 | 1 | 单总线通信 |
| 12 | 数码管 | 两位共阴 | 1 | 显示温度或占空比 |
| 13 | 电源 | 5V/1A | 1 | 单片机与电机分路供电 |
5.2 程序流程图与中断优先级设计
全系统只有两个中断源:定时器0中断输出 PWM,温度采集期间临时关闭定时器0中断。主循环工作流程是:初始化各模块,读取 DS18B20,按键扫描更新 manual_pulse,调用温度控制合成 final_pulse,刷新数码管,循环往复。画流程图时要注意把 PWM 输出画成独立分支,不要画进主循环,因为主循环里任何一条延时语句都会阻塞十个 PWM 周期,波形上会出现明显毛刺。
答辩时判断流程图是否合格的标准是看“温度读取放哪一步”。放在主循环头部的是正确画法,放进中断服务程序的属于概念错误:单总线读写本身要关中断,再把读取放回中断等于把系统卡死在嵌套里。流程图里还要画出异常分支:DS18B20 复位失败时显示错误代码并重试,而不是继续执行读温度,否则温度值会保持上一次的残留数据。
5.3 用 Proteus 虚拟示波器验证 PWM 调制的最后一步
仿真运行后,从左侧工具栏选择虚拟仪器 OSCILLOSCOPE,把 A 通道探针接到 P1.0,地线接到 GND,运行后能看到一组频率约 1kHz 的方波。按加速键观察高电平宽度逐步变宽,这就是占空比可调的直接证据。再把示波器 A 通道切换为平均值测量,按键从低速扫到高速,平均值应该是一条平滑上升的斜坡。如果平均值出现台阶,先查 pulse 变量的赋值时机:按键扫描和温度控制都在改 pulse,最终输出前必须经过限幅,限幅之后的 pulse 不能再被其他分支污染。把示波器显示比例调到 1ms/格,对比加速前后的占空比数值,与代码里的 pulse 值完全一致时,这个项目就具备实物移植条件了。
本文还有配套的精品资源,点击获取