【Protues仿真】基于AT89C52单片机的舵机和直流电机控制
目录
1 PWM信号
1.1 三个最基本的量
1.1.1 周期 T(Period)
1.1.2脉冲宽度 Th(High Time)
1.1.3占空比 D(Duty Cycle)
1.2 为什么要用 PWM
1.3 关键参数对照表
1.4单片机里产生 PWM 的四种套路
1.4.1 纯软件延时
1.4.2 定时器中断计数
1.4.3 定时器硬件 PWM 模块
1.4.4 专用 PWM IC 或驱动板
1.5 计算示例(AT89C52,12 MHz,定时器 0 模式 1)
1.6 常见踩坑提醒
1.7 一句话总结
2 AT89C52单片机实现舵机控制
2.1 舵机专用 PWM(50 Hz)
2.2 电路原理图
2.3 控制程序
3 AT89C52单片机实现直流电机控制
3.1L298N电机驱动
3.1.1它到底是什么
3.1.2引脚速记
3.1.3控制逻辑真值表(电机 A 为例)
3.1.4 AT89C52 示例代码(定时器 0 产生 1 kHz PWM)
3.1.5 常见坑 & 快速排查
3.2 单片机实现直流电机控制电路原理图
3.3 单片机实现直流电机控制控制程序
摘要:本文系统介绍了PWM技术原理及其在舵机和直流电机控制中的应用。PWM通过调节脉冲宽度实现模拟量控制,关键参数包括周期T、高电平时间Th和占空比D。文中详细阐述了AT89C52单片机实现PWM的四种方法,重点展示了50Hz舵机控制(0.5-2.5ms脉宽对应-90°至+90°)和L298N驱动直流电机的具体实现方案,包含完整的电路原理图和C语言程序代码。特别指出应用中需注意的周期选择、驱动能力、电平匹配等常见问题,并提供了参数计算示例和调试建议。
1 PWM信号
PWM(Pulse Width Modulation,脉冲宽度调制)是一种用「数字脉冲」去等效「模拟量」的技术。
1.1 三个最基本的量
1.1.1 周期 T(Period)
一个完整脉冲循环的时间,单位 μs 或 ms。
例:舵机 50 Hz → T = 20 ms。
1.1.2脉冲宽度 Th(High Time)
一个周期内输出高电平的时间。
例:舵机 1.5 ms。
1.1.3占空比 D(Duty Cycle)
D = Th / T × 100 %
例:1.5 ms / 20 ms = 7.5 %。
1.2 为什么要用 PWM
1 数字口只有 0/1,无法直接输出 3.3 V 的一半电压。
2 通过高速开关,让「平均电压」等于目标值:
Vavg = Vhigh × D
例:5 V、占空比 50 % → 等效 2.5 V(电机速度减半、LED 半亮)。
1.3 关键参数对照表
应用 | 周期 T | 常用 Th 范围 | 分辨率要求 | 备注 |
LED 调光 | 1~5 kHz | 0~100 % | 8 bit | 频率太低会闪 |
直流电机调速 | 10~20 kHz | 0~100 % | 8~10 bit | 频率>20 kHz 避人耳噪声 |
舵机控制 | 20 ms | 0.5~2.5 ms | 1 μs | 50 Hz 固定 |
开关电源(Buck) | 50 k~2 MHz | 根据拓扑 | 10~12 bit | MOSFET 驱动 |
1.4单片机里产生 PWM 的四种套路
1.4.1 纯软件延时
for/while + delay → 最简单、最不准、CPU 100 % 占用。
1.4.2 定时器中断计数
每 x μs 进一次中断,累加变量 → 精度高,可做多通道。
1.4.3 定时器硬件 PWM 模块
51 系列没有,AVR/STM32 有 OCR/ARR 寄存器,直接硬件输出。
1.4.4 专用 PWM IC 或驱动板
PCA9685(I²C 16 路)、DRV8833 等,省 IO,精度高。
1.5 计算示例(AT89C52,12 MHz,定时器 0 模式 1)
目标:50 Hz,1 μs 分辨率
计数频率:12 MHz / 12 = 1 MHz → 1 μs/次
20 ms 对应计数:20 000 次
16 位定时器最大 65 536,够用。
步骤:
设 TH0 = (65536-20000)/256,TL0 = … 先装 20 ms 周期。
再开一个「比较寄存器」存放 Th(如 1500)。
中断里计数到 Th 翻转 IO,到 20 000 再翻转 IO,循环即可。
1.6 常见踩坑提醒
周期太低 → LED/电机肉眼可见抖动或蜂鸣。
Th 精度太差 → 舵机抖动、稳速电机转速漂移。
IO 驱动能力不足 → 加三极管/MOSFET 做缓冲。
舵机高电平 5 V,STM32 单片机 3.3 V 输出时,最好加 NPN 或电平转换。
1.7 一句话总结
PWM = 「固定周期 + 可变高电平」,用数字开关把平均电压/角度/速度「模拟化」。掌握 T、Th、D 三要素,再挑合适的「生成套路」,任何 PWM 场景都能秒拆。
2 AT89C52单片机实现舵机控制
2.1 舵机专用 PWM(50 Hz)
周期:T = 20 ms(50 Hz)
有效高电平:0.5 ms ~ 2.5 ms(有些舵机 0.4 ms ~ 2.6 ms)
角度映射:
0.5 ms → −90°
1.5 ms → 0°
2.5 ms → +90°
分辨率:舵机内部一般 1 µs 对应 0.09°,所以 10 µs 步进肉眼基本看不出抖动。
2.2 电路原理图
如上图所示,AT89C52单片机实现舵机控制电路由AT89C52单片机、晶振及复位电路、舵机、控制开关和示波器组成。通过PWM信号实现舵机转角的控制,不同开关导通情况下P2.5控制信号的占空比不同,从而实现舵机转角的控制。
2.3 控制程序
//头文件与为位定义
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned charuchar Compare=200;//50HZ 抑芷?0ms
uint ZHK;//PWM信号占空比sbit k2=P2^0;//-90度
sbit k3=P2^1;//0度
sbit k4=P2^2;//90度
sbit k1=P2^5;//舵机控制信号//定时器T0产生 100μs 中断
void Timer0_init()//100us
{TMOD= 0x01; //设置定时器模式TL0 = 0x9B; //设置定时初值低位TH0 = 0xFF; //设置定时初值高位 差值表示中断周期ET0=1;EA=1; TR0=1;//启动定时器
}//定时器延时函数
void Delay(unsigned int ms)
{unsigned int i;for (i = 0; i < ms; i++){while (!TF0); // 等待定时器T0溢出TF0 = 0; // 清除溢出标志}
}//定时器T0中断服务函数
void Timer0() interrupt 1
{TL0 = 0x9B; //设置定时初值低位TH0 = 0xFF; //设置定时初值高位}
//舵机转到-90度
void chushi()
{ZHK=196;k1=0;Delay(ZHK);k1=1;Delay(Compare-ZHK);
}
//舵机转到0度
void zhongjian()
{ZHK=186;k1=0;Delay(ZHK);k1=1;Delay(Compare-ZHK);
}
//舵机转到90度
void zuizhong()
{ZHK=175;k1=0;Delay(ZHK);k1=1;Delay(Compare-ZHK);
}
//主函数
void main()
{Timer0_init();while(1){if(k2==0){chushi();}else if(k3==0){zhongjian();}else if(k4==0){zuizhong();}}
}
3 AT89C52单片机实现直流电机控制
3.1L298N电机驱动
3.1.1它到底是什么
一块“双H-桥”功率芯片(ST 原装 L298N)+ 外围电路做成的驱动板。
可同时驱动:
两台直流有刷电机,或一台四线两相步进电机。
极限参数:逻辑 5 V 供电,驱动端 5-35 V(建议 ≤24 V 长期工作),连续 2 A/桥,峰值 3 A。
3.1.2引脚速记
Power 区域
VS→ 外接 7-24 V 电机电源
GND→ 与单片机共地
VCC→ 三种用法
① 板上跳线帽插上:内部线性稳压给逻辑 5 V(仅当 VIN ≤12 V 时可用);
② 拔掉跳线帽:由外部单独 5 V 供逻辑;
③ 反向输出:板上稳压 5 V 可引出来给单片机供电(仍要求 VIN ≤12 V)。
Motor 区域
OUT1/OUT2→ 电机 A
OUT3/OUT4→ 电机 B
Logic 区域
IN1/IN2/ENA→ 控制电机 A 的方向与使能
IN3/IN4/ENB→ 控制电机 B
(ENA/ENB 接 PWM 即可调速)
3.1.3控制逻辑真值表(电机 A 为例)
ENA | IN1 | IN2 | 结果 |
0 | X | X | 刹车(自由) |
1 | 1 | 0 | 正转 |
1 | 0 | 1 | 反转 |
1 | 1 | 1 | 快速刹车 |
PWM | 1/0 | 0/1 | 调速+方向 |
3.1.4 AT89C52 示例代码(定时器 0 产生 1 kHz PWM)
#include <reg52.h>
sbit IN1 = P1^0;
sbit IN2 = P1^1;
sbit ENA = P1^2; // 接 L298N 的 ENA
bit pwm_out = 0;
unsigned char high = 200; // 占空比 200/255 ≈ 78 %
void Timer0_Init()
{TMOD &= 0xF0; // 模式 2,8 位自动重装TH0 = 256-100; // 100*1us = 100 us → 10 kHzTL0 = 256-100;ET0 = 1; EA = 1;TR0 = 1;
}
void Timer0_ISR() interrupt 1
{static unsigned char cnt = 0;cnt++;if(cnt == 0) pwm_out = 1; // 周期起点if(cnt == high) pwm_out = 0;ENA = pwm_out;
}
void main()
{IN1 = 1; IN2 = 0; // 方向:正转Timer0_Init();while(1){/* 可在此修改 high 值调速,或改 IN1/IN2 换向 */}
}
3.1.5 常见坑 & 快速排查
现象 | 90 % 原因与对策 |
电机不转,指示灯不亮 | 没给 +12V;跳线帽错;共地问题 |
电机抖动、咔咔响 | 电源功率不足;PWM 频率太低 |
芯片过热、保护 | 电流 >2 A;散热片没装;占空比 100 % 长时间堵转 |
逻辑失控 | 单片机 5 V 与驱动板 5 V 没共地;ENA 悬空 |
3.2 单片机实现直流电机控制电路原理图
如上图所示,AT89C52单片机实现直流电机控制电路由AT89C52单片机、L298N电机驱动、开关控制电路、晶振及复位电路、输出电压显示、示波器和直流电机组成。通过软件设置不同占空比的PWM信号实现直流电机转速的控制,再通过L298N电机驱动的驱动可以实现直流电机正转、反转、停止、加速和减速的控制。
3.3 单片机实现直流电机控制控制程序
//头文件与为位定义
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned charuchar Compare=20;//50HZ 一周期20ms
uint ZHK1;//正转占空比
uint ZHK2;//反转占空比sbit IN1=P1^0;
sbit IN2=P1^1;
sbit ENA=P1^2; sbit k0=P2^0;//正转
sbit k1=P2^1;//反转
sbit k2=P2^2;//停止
sbit k3=P2^3;//正传加速
sbit k4=P2^4;//正传减速
sbit k5=P2^5;//反转加速
sbit k6=P2^6;//反转减速//定时器T0产生 1000μs 中断
void Timer0_init()//1000us
{TMOD= 0x01; //设置定时器模式TL0 = 0x17; //设置定时初值低位TH0 = 0xFC; //设置定时初值高位 差值表示中断周期ET0=1;EA=1; TR0=1;//启动定时器
}//定时器延时函数
void Delay(unsigned int ms)
{unsigned int i;for (i = 0; i < ms; i++){while (!TF0); // 等待定时器T0溢出TF0 = 0; // 清除溢出标志}
}//定时器T0中断服务函数
void Timer0() interrupt 1
{TL0 = 0x17; //设置定时初值低位TH0 = 0xFC; //设置定时初值高位}
//电机正转
void DJZHZH()
{ENA=1;ZHK1=10;IN1=1;IN2=0;Delay(ZHK1);IN1=0;IN2=0;Delay(Compare-ZHK1);
}
//电机反转
void DJFZH()
{ENA=1;ZHK2=10;IN1=0;IN2=1;Delay(ZHK2);IN1=0;IN2=0;Delay(Compare-ZHK2);
}
//停
void DJT()
{ENA=0;TR0=0;IN1=0;IN2=0;
}
//正转加速
void DJZHZH1()
{ENA=1;ZHK1=18;IN1=1;IN2=0;Delay(ZHK1);IN1=0;IN2=0;Delay(Compare-ZHK1);
}
//电机反转加速
void DJFZH1()
{ENA=1;ZHK2=18;IN1=0;IN2=1;Delay(ZHK2);IN1=0;IN2=0;Delay(Compare-ZHK2);
}
//电机正传减速
void DJZHZH2()
{ENA=1;ZHK1=2;IN1=1;IN2=0;Delay(ZHK1);IN1=0;IN2=0;Delay(Compare-ZHK1);
}
//电机反转减速
void DJFZH2()
{ENA=1;ZHK2=2;IN1=0;IN2=1;Delay(ZHK2);IN1=0;IN2=0;Delay(Compare-ZHK2);
}void main()
{DJT(); Timer0_init();while(1){if(k0==0)//正转{DJZHZH();}else if(k1==0)//反转{DJFZH();}else if(k2==0) //停止{DJT(); } else if(k3==0)//正转加速{DJZHZH1(); } else if(k5==0)//反转加速{DJFZH1(); } else if(k4==0)//正转减速{DJZHZH2(); } else if(k6==0)//反转减速{DJFZH2(); } }
}