【51单片机实战】PWM调速、AD/DA转换与红外遥控的综合应用设计

张开发
2026/4/11 8:26:47 15 分钟阅读

分享文章

【51单片机实战】PWM调速、AD/DA转换与红外遥控的综合应用设计
1. PWM调速在51单片机中的实战应用PWM脉冲宽度调制是控制直流电机速度最常用的方法之一。我第一次用51单片机做电机控制时发现PWM真是个神奇的东西 - 它能让电机乖乖听话想快就快想慢就慢。简单来说PWM就是通过快速开关来控制电机的平均功率开关越快电机转得越稳。在51单片机上实现PWM通常有两种方式软件模拟和硬件定时器。软件模拟就是在主循环里不断翻转IO口这种方法简单但会占用大量CPU资源。更专业的做法是利用定时器中断生成PWM信号这样主循环还能干其他事情。我做过一个实验用定时器0产生10kHz的PWM波驱动电机效果相当稳定。这里有个实际项目中的经验PWM频率选择很关键。频率太低电机会抖动太高又会产生额外的开关损耗。经过多次测试我发现对于普通直流电机10-20kHz是个不错的范围。但要注意51单片机的主频较低设置太高频率可能会导致精度不够。// 定时器初始化代码示例 void Timer0_Init() { TMOD 0xF0; TMOD | 0x01; // 设置定时器0为模式1 TH0 0xFF; // 设置定时初值 TL0 0x9C; ET0 1; // 开启定时器0中断 EA 1; // 开启总中断 TR0 1; // 启动定时器0 } // 中断服务函数中的PWM生成逻辑 void Timer0_ISR() interrupt 1 { static unsigned char counter 0; TH0 0xFF; TL0 0x9C; counter; if(counter compare_value) { MOTOR_PIN 1; // 输出高电平 } else { MOTOR_PIN 0; // 输出低电平 } if(counter 100) counter 0; }电机驱动电路同样重要。直接连接单片机IO口是绝对不行的 - 电机的启动电流很大会损坏单片机。我推荐使用L298N这类电机驱动模块它内置了H桥电路不仅能驱动电机还能控制转向。记得要加续流二极管防止电机断电时产生的反向电动势损坏电路。2. AD/DA转换模块的集成与应用AD转换让我们能把模拟世界的信息带入数字系统。在智能小车项目中我常用光敏电阻和热敏电阻来感知环境光照和温度。51单片机本身没有内置ADC所以需要外接ADC芯片比如XPT2046这款芯片价格便宜且性能稳定。XPT2046是SPI接口的12位ADC支持多通道输入。我第一次用它时遇到了个坑采样结果总是不稳定。后来发现是电源噪声问题加了几个滤波电容后就稳定了。这款芯片的另一个优点是内置了触摸屏控制功能如果你的项目需要触摸输入它能一芯多用。// XPT2046读取AD值的代码示例 unsigned int XPT2046_ReadAD(unsigned char channel) { unsigned char i; unsigned int data 0; XPT2046_CS 0; // 使能芯片 // 发送控制字节 for(i0; i8; i) { XPT2046_DIN channel (0x80i); XPT2046_CLK 1; XPT2046_CLK 0; } // 读取转换结果 for(i0; i16; i) { XPT2046_CLK 1; XPT2046_CLK 0; if(XPT2046_DOUT) data | (0x8000i); } XPT2046_CS 1; // 禁用芯片 return data8; // 返回12位有效数据 }DA转换在51单片机项目中用得相对较少因为很多场合可以用PWM加滤波电路来模拟模拟量输出。不过当需要精确的电压输出时DAC芯片还是不可替代的。我常用DAC0832它价格便宜且接口简单。记得在输出端加个电压跟随器增强驱动能力。AD/DA转换都要注意参考电压的稳定性。我曾遇到AD采样不准的问题折腾了好久才发现是参考电压引脚没接滤波电容。建议在VREF引脚接个10uF的钽电容并联0.1uF的陶瓷电容这样能获得更稳定的转换结果。3. 红外遥控的实现与解码红外遥控为项目增加了无线控制的便利性。NEC编码是红外遥控中最常见的协议几乎所有家电遥控器都采用这种编码方式。在51单片机项目中我通常使用HS0038这类一体化红外接收头它内部已经集成了放大、滤波和解调电路使用起来非常简单。红外解码的关键是准确测量脉冲宽度。我推荐使用外部中断加定时器的方式这样既准确又不会占用太多CPU资源。具体做法是将红外接收头的输出接到单片机的外部中断引脚如P3.2设置为下降沿触发。当检测到下降沿时启动定时器测量到下一个下降沿的时间间隔根据这个时间判断是逻辑0、逻辑1还是起始信号。// 红外解码的状态机实现 void IR_Decode() interrupt 0 { static unsigned char state 0; unsigned int pulse_width Timer0_GetCounter(); Timer0_SetCounter(0); switch(state) { case 0: // 等待起始信号 if(pulse_width 12000 pulse_width 14000) { state 1; // 检测到起始信号 } break; case 1: // 接收数据位 if(pulse_width 1000 pulse_width 1300) { // 识别为逻辑0 ir_data[data_index] ~(1bit_index); } else if(pulse_width 2000 pulse_width 2300) { // 识别为逻辑1 ir_data[data_index] | (1bit_index); } bit_index; if(bit_index 32) { // 完成一帧数据的接收 state 0; data_ready 1; } break; } }实际项目中红外接收容易受到环境光干扰。我发现两个改善接收稳定性的技巧一是给接收头套上黑色热缩管减少环境光干扰二是在软件中加入简单的纠错机制比如检查地址码和命令码的反码是否正确。另外遥控器的按键会有抖动可以在解码后加入50ms左右的防抖延时。4. 三大模块的综合应用设计将PWM调速、AD/DA转换和红外遥控整合到一个项目中最能体现51单片机的强大功能。我设计过一个智能小车系统可以通过红外遥控调节速度同时实时监测环境光强并自动调节车灯亮度。这个项目完美融合了三大模块下面分享关键设计思路。硬件连接方面电机驱动模块接P1.0口用于PWM输出XPT2046接P3.4-P3.7实现SPI通信红外接收头接P3.2外部中断引脚。注意要给每个模块提供稳定的电源电机部分最好单独供电避免干扰其他电路。软件架构采用分层设计底层是驱动层包括PWM生成、AD转换和红外解码中间是控制层处理用户输入和系统状态上层是应用层实现具体功能逻辑。这种结构清晰且易于维护。我特别推荐使用状态机编程思想它能让复杂逻辑变得简单明了。// 综合应用的主循环示例 void main() { System_Init(); // 初始化所有外设 while(1) { // 处理红外遥控 if(IR_DataReady()) { unsigned char cmd IR_GetCommand(); Handle_Command(cmd); // 执行相应操作 } // 读取环境光强 unsigned int light XPT2046_ReadAD(LIGHT_CH); Adjust_Light(light); // 根据光强调节车灯 // 其他任务... Display_Update(); } }调试这种综合项目时我总结出一个有效的方法分模块调试先确保每个功能单独工作正常再逐步整合。比如先调通PWM电机控制再加入AD采样最后整合红外遥控。每完成一个阶段都进行充分测试。记得多用示波器观察信号波形很多奇怪的问题都能通过波形分析找到原因。电源管理是另一个需要注意的重点。当电机启动时会产生较大的电流波动可能导致单片机复位。我的解决方案是在电源处加大容量储能电容如470uF并在单片机VCC引脚加0.1uF的去耦电容。如果条件允许最好使用单独的LDO为单片机供电。

更多文章