目錄
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(I2C 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(); } }
}