51单片机之PWM(呼吸灯、电机、舵机驱动)

一、PWM概念

PWM(Pulse Width Modulation,脉宽调制) 是一种通过调节脉冲的占空比(高电平时间占整个周期的比例)来模拟不同电压或功率输出的技术。

  • 频率(Frequency):1秒内脉冲重复的次数(单位:Hz)。     频率=1/Ts

  • 占空比(Duty Cycle):高电平时间占周期的百分比(范围:0%~100%)。 占空比=Ton/Ts

  • 分辨率:占空比的最小调节步长(由定时器位数决定)。

  • 精度:占空比变化的步距。

二、51单片机实现PWM

        51单片机通常无硬件PWM模块,需通过软件模拟定时器+IO口实现:

1.纯软件模拟PWM

(占用CPU资源,精度低,频率受限)

#include <reg51.h>
#include "delay.h"
void main(void)
{
 unsigned char t;
 while(1)
   {
    for(t=0;t<100;t++)
      {
       P1 = 0xFF;  //P1口为高电平
       delay(t);  //高电平时间
       p1 = 0x00;    //P1口为高电平
       delay(100-t);  //高电平时间,假设周期为100
       }

   }
}

2.定时器+IO口实现PWM

(精度高,可后台运行,支持多通道)

#include <reg51.h>
#include "delay.h"
void timer0_init(void)
{
 TMOD &= 0xF0;
 TMOD |= 0x01; //设置定时器模式
 TL0 = 0x9C;
 TH0 = 0xFF;  //设置初值
 TF0 = 0;     //清除中断标志
 TR0 = 1;     //开始计时
 ET0 = 1;     //开启定时中断
 EA = 1;      //开启总中断
 PT0 = 0;     //选择优先级
}
unsigned char counter = 0;
unsigned char compare =50; //设置占空比50
void main(void)
{
  timer0_init();  
  while(1)
    {
   
    }

}

void timer0_ISR()  interrupt 1  //中断函数
{
 TL0 = 0x9C;
 TH0 = 0xFF;  //重装初值
 counter ++;
 conter %= 100;  //当conter为100时清零
 if(conter<compre)
  {
   P1 = 0;  //P1口输出低电平
  }
 else
  {
   P1 = 1;  //P1口输出高电平
  }

}

三、PWM应用

1.LED呼吸灯

#include <reg51.h>
#include <intrins.h>

sbit LED = P1^0;        // LED连接P1.0
volatile unsigned int PWM_Duty = 0;  // 占空比(0~100)
volatile bit Direction = 0;          // 呼吸方向:0递增,1递减

// 定时器0初始化(模式2,自动重载)
void Timer0_Init() 
{
    TMOD &= 0xF0;       // 清除T0原有配置
    TMOD |= 0x02;       // 模式2(8位自动重载)
    TH0 = 0x9C;         // 重载值,100us中断一次(12MHz晶振)
    TL0 = 0x9C;         // 初始值
    ET0 = 1;            // 允许定时器0中断
    EA = 1;             // 全局中断使能
    TR0 = 1;            // 启动定时器0
}

// 定时器0中断服务函数
void Timer0_ISR() interrupt 1 
{
    static unsigned int counter = 0;
    counter++;
    if (counter >= 100) counter = 0;  // PWM周期=100*100us=10ms(100Hz)
    
    LED = (counter < PWM_Duty) ? 1 : 0; // 根据占空比控制LED亮灭
}

// 简单延时函数(12MHz晶振)
void Delay_ms(unsigned int ms) 
{
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 114; j++);
}

void main() 
{
    Timer0_Init();      // 初始化定时器
    while (1) 
    {
        // 调整占空比,实现呼吸效果
        if (!Direction) {
            PWM_Duty++;             // 渐亮
            if (PWM_Duty >= 100) Direction = 1;
        } else {
            PWM_Duty--;             // 渐暗
            if (PWM_Duty <= 0) Direction = 0;
        }
        // 控制呼吸速度(约每20ms调整一次)
        Delay_ms(20);    // 使用简单延时或定时器优化
    }
}

2.PWM驱动直流电机

#include <reg51.h>

sbit ENA = P1^0;  // PWM使能端
sbit IN1 = P1^1;   // 方向控制1
sbit IN2 = P1^2;   // 方向控制2

volatile unsigned char PWM_Duty = 0;  // 占空比(0-100)

// 定时器0初始化(模式2自动重载,100Hz PWM)
void Timer0_Init() {
    TMOD &= 0xF0;     // 清除T0配置
    TMOD |= 0x02;     // 模式2(8位自动重载)
    TH0 = 0x9C;       // 100us中断(12MHz晶振)
    TL0 = 0x9C;
    ET0 = 1;          // 允许定时器0中断
    EA = 1;           // 全局中断使能
    TR0 = 1;          // 启动定时器
}

// 定时器0中断服务函数
void Timer0_ISR() interrupt 1 {
    static unsigned char counter = 0;
    counter++;
    if(counter >= 100) counter = 0;
    ENA = (counter < PWM_Duty) ? 1 : 0;  // 输出PWM
}

// 设置电机转速和方向
void Motor_Control(unsigned char speed, bit direction) 
{
    PWM_Duty = speed;  // 速度控制(0-100)
    if(direction) {     // 正转
        IN1 = 1;
        IN2 = 0;
    } else {            // 反转
        IN1 = 0;
        IN2 = 1;
    }
}

// 停止电机
void Motor_Stop() 
{
    IN1 = 0;
    IN2 = 0;
    PWM_Duty = 0;
}

void main() 
{
    Timer0_Init();      // 初始化PWM
    Motor_Control(70, 1);  // 70%速度正转
    Delay_ms(3000);        // 运行3秒
    Motor_Control(50, 0);  // 50%速度反转
    Delay_ms(2000);
    Motor_Stop();          // 停止
    while(1);
}

3.PWM控制舵机

舵机通过脉冲宽度调制(PWM)控制旋转角度,其核心参数如下:

工作周期:通常为 20ms(50Hz),部分高速舵机支持10ms(100Hz)。

脉冲宽度范围

        0.5ms → 0°(最小角度)

        1.5ms → 90°(中位)

        2.5ms → 180°(最大角度)

控制精度:多数舵机分辨率为 1°~2°,高端舵机可达0.5°。

#include <reg51.h>

sbit Servo_PWM = P1^0;  // 舵机信号线接P1.0
unsigned int PWM_Width = 1500;  // 初始脉冲宽度1.5ms(90°)

// 简易延时函数(12MHz)
void Delay_ms(unsigned int ms) 
{
    unsigned int i, j;
    for(i=0; i<ms; i++)
        for(j=0; j<114; j++);
}

// 定时器0初始化(模式1,16位定时器)
void Timer0_Init() 
{
    TMOD &= 0xF0;       // 清除T0配置
    TMOD |= 0x01;       // 模式1(16位定时器)
    TH0 = (65536 - 1000) / 256;  // 1ms中断(12MHz晶振)
    TL0 = (65536 - 1000) % 256;
    ET0 = 1;            // 允许定时器0中断
    EA = 1;             // 全局中断使能
    TR0 = 1;            // 启动定时器
}

volatile unsigned int count = 0;
void Timer0_ISR() interrupt 1 
{
    TH0 = (65536 - 1000) / 256;  // 重载1ms初值
    TL0 = (65536 - 1000) % 256;
    count++;
    
    if(count <= PWM_Width / 1000) {  // 高电平时间(单位:ms)
        Servo_PWM = 1;
    } else {
        Servo_PWM = 0;
    }
    
    if(count >= 20) count = 0;  // 周期20ms(50Hz)
}

// 设置舵机角度(0°~180°)
void Set_Servo_Angle(unsigned char angle) 
{
    if(angle > 180) angle = 180;
    PWM_Width = 500 + angle * 11.11;  // 公式:0.5ms + angle*(2ms/180)
    // 500=0°, 2500=180°(实际值需校准)
}

// 示例:让舵机从0°到180°扫描
void main() 
{
    Timer0_Init();
    while(1) {
        for(int i=0; i<=180; i++) {
            Set_Servo_Angle(i);
            Delay_ms(20);  // 每20ms移动1°
        }
        for(int i=180; i>=0; i--) {
            Set_Servo_Angle(i);
            Delay_ms(20);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值