51单片机电机控制模拟(6)PWM简介

脉冲宽度调制(Pulse width Modulation或 PWM) 是当今控制系统中使用的强大技术之一。它广泛应用于速度控制、功率控制、测量和通信。本文将带您了解脉冲宽度调制的基础知识及其在微控制器上的实现。PWM 的基本原理

脉冲宽度调制基本上是一个具有变化的高低时间的方波。下图显示了一个基本的 PWM 信号。

Image

PWM 有各种相关术语:

开启时间(On-Time):信号为高电平的持续时间

关闭时间(Off-Time):信号为低电平的持续时间

周期(Period):表示为 PWM 信号开启时间和关闭时间的总和

占空比(Duty Cycle):表示为 PWM 信号周期内信号保持开启的时间百分比

周期

如图所示,Ton 表示信号的开启时间,Toff 表示信号的关闭时间。周期是开启和关闭时间的总和,计算公式如下:

占空比

占空比开启时间占整个周期的比例。使用上面计算的周期,占空比计算如下:

Image

PWM:电压调节

PWM 信号在不同占空比下使用时,输出端的电压会发生变化。此方法用于各种应用领域,例如:

  • 开关调节器
  • LED 调光器
  • 音频
  • 模拟信号生成
  • 等等…

电压调节是通过平均 PWM 信号来完成的。输出电压由以下公式表示:

Image

从公式中可以看出,通过改变 Ton 值可以直接改变输出电压。

如果 Ton 为 0,则 Vout 也为 0。如果 Ton 为 Ttotal,则 Vout 为 Vin 或最大值。

在 8051 上实现 PWM

在 8051 上实现 PWM 的基本思想是使用定时器并以定义的间隔切换端口引脚高/低。正如我们在 PWM 简介中所讨论的那样,通过改变 Ton 时间,我们可以改变方波的宽度,同时保持方波的时间周期相同。

我们将在模式 0 中使用 8051 Timer0。高电平和低电平的值将以总延迟保持不变的方式加载。如果对于高电平,我们在 TH0 中加载一个值 X,那么对于低电平,TH0 将加载 255-X,这样总延迟仍为 255。

(注:有关单片机定时器的介绍可以查看这篇文章)

C程序代码

/* Global variables and definition */
#include <reg51.h>  // Include 8051 register definitions
sbit PWMPIN = P1^0;  // Define P1^0 as PWM output pin
unsigned char pwm_width;  // PWM duty cycle width
bit pwm_flag = 0;         // Flag to toggle high/low state
void pwm_setup()
{
    TMOD = 0;          // Set Timer 0 in Mode 0 (13-bit mode)
    pwm_width = 128;   // Set duty cycle to 50% (high time = low time)
    EA = 1;            // Enable global interrupts
    ET0 = 1;           // Enable Timer 0 interrupt
    TR0 = 1;           // Start Timer 0
}
/* Timer 0 Interrupt Service Routine */
void timer0() interrupt 1
{
    if (!pwm_flag) {            // Start of High level
        pwm_flag = 1;           // Set flag
        PWMPIN = 1;             // Set PWM output pin high
        TH0 = pwm_width;        // Load timer with high time
        TF0 = 0;                // Clear interrupt flag
    } else {                    // Start of Low level
        pwm_flag = 0;           // Clear flag
        PWMPIN = 0;             // Set PWM output pin low
        TH0 = 255 - pwm_width;  // Load timer with low time
        TF0 = 0;                // Clear interrupt flag
    }
}
void pwm_stop()
{
    TR0 = 0;  // Disable Timer 0 to stop PWM
}
/* Main function */
void main()
{
    pwm_setup();  // Set up PWM
    while (1) {
        // Main loop can perform other tasks
    }
}

先熟悉一下以下关键字:

TMOD:定时器模式寄存器,是一个8位宽的寄存器。低4位用于定时器0,高4位用于定时器1,TMOD=0表示启用定时器0模式0。

TH0: 定时器0寄存器高字节(8位)。

TF0: 定时器0控制寄存器(TCON)溢出标志位。当定时器溢出(从逻辑1过渡到逻辑0时)时,溢出标志TF将被设置为1。

TR0: 定时器0控制寄存器(TCON)启动控制位,0为停止,1为启动。

EA: 中断使能寄存器第7位(使能位),这个位必须设置为1以启用所有中断,0则会禁用所有中断。

ET0: 定时器0中断位, 1为启用,0为禁止。

再来看这段代码:

void pwm_setup()
{
    TMOD = 0;          // Set Timer 0 in Mode 0 (13-bit mode)
    pwm_width = 128;   // Set duty cycle to 50% (high time = low time)
    EA = 1;            // Enable global interrupts
    ET0 = 1;           // Enable Timer 0 interrupt
    TR0 = 1;           // Start Timer 0
}

所以以上代码的作用就是设置定时器0为模式0,设置好全局中断,设置好定时器0中断,启动定时器。

再来看这段中断服务代码(有关单片机中断的介绍可以查看这篇文章):

void timer0() interrupt 1
{
    if (!pwm_flag) {            // Start of High level
        pwm_flag = 1;           // Set flag
        PWMPIN = 1;             // Set PWM output pin high
        TH0 = pwm_width;        // Load timer with high time
        TF0 = 0;                // Clear interrupt flag
    } else {                    // Start of Low level
        pwm_flag = 0;           // Clear flag
        PWMPIN = 0;             // Set PWM output pin low
        TH0 = 255 - pwm_width;  // Load timer with low time
        TF0 = 0;                // Clear interrupt flag
    }
}

其中interrupt 1也是一个关键字,他是跟计时器0溢出相关联的中断服务interrupt service routine (ISR)。

8051具有以下中断源及其对应的编号:

Interrupt NameInterrupt NumberTrigger Condition
External Interrupt 00Signal on INT0 pin (P3.2)
Timer 0 Overflow1Timer 0 overflows
External Interrupt 12Signal on INT1 pin (P3.3)
Timer 1 Overflow3Timer 1 overflows
Serial Communication4Serial receive or transmit event

当单片机检测到中断(例如,定时器 0 溢出)。它将暂停当前程序的执行并跳转到与中断号关联的 ISR(例如,定时器 0 溢出的中断 1)。一旦 ISR 完成,单片机将恢复主程序。

定时器溢出

当定时器寄存器达到其最大值(例如,8 位定时器的最大值是 255)并收到另一个时钟脉冲时,它会“溢出”并返回到 0。此事件称为定时器溢出。

程序运行逻辑如下:

程序启动->计时器0溢出->触发中断1->设置PWM针脚高电平->重置计时器值为128并开始重新计时->清理溢出标志->计时器0溢出->触发中断1->设置PWM针脚低电平->重置计时器值为127并开始重新计时->清理溢出标志

Ton = 2^13-128 = 8192-128 = 8064 tick ≈ 8ms

Toff = 2^13-127 = 8192-127 = 8065 tick≈ 8ms

Ttotal(Period ) = Ton + Toff ≈ 16ms

在Proteus中建立如下简单电路图并运行程序:Image

示波器显示占空比为50%的PWM方波:Image