单片机测量PWM占空比的三种方法

单片机测量PWM占空比的三种方法

PWM(Pulse Width Modulation),一般指脉冲宽度调节,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中,比如LED亮度调节、电机转速控制等。
而在某些特殊应用中,我们也需要通过测量输入PWM的占空比,来实现不同的输出控制,这就需要使用到PWM占空比的测量方法。这里介绍三种不同的测量方法:阻塞方式、中断方式以及定时器捕获功能。

1. 阻塞方式

MCU阻塞方式测量PWM占空比的原理比较简单,也只需要使用到一个普通的IO端口(设置为输入模式,对于51而言那就是一个普通的双向口)。具体实现流程为:

  • 等待上升沿到来,然后开启定时器,开始计时;
  • 等待下降沿到来,记录下定时器的计数值,即得到PWM的高电平时间H
  • 同时,清零定时器,重新开始计数;
  • 等待上升沿到来,记录下定时器的计数值,即得到PWM的低电平时间L
  • 计算得出占空比:duty = H / (H + L);

阻塞方式原理简单,而且只需要MCU有一个定时器的资源即可实现;但采集时阻塞CPU运行,阻塞的时间和输入PWM的周期相关,只适用于实时性较低的系统。

另外,上述流程中存在着一个严重的BUG,即当输入的PWM占空比为0%或者100%时,程序会被一直阻塞,等待上升沿/下降沿的到来。所以解决方法是,在等待上升沿/下降沿的过程中,实时提取定时器的值,一旦定时时间超过1个周期的限定(一般可定义为2-3个周期时间),即退出等待,并根据端口电平判断此时占空比为0%(低电平)或100%(高电平)

示例代码,仅供参考:

//获取PWM输入脚的电平
#define PWM_IN()   xxxxxx
//定义超时时间(如2-3倍PWM周期)
#define T1_TIMEOUT  xxxxxx

uint8_t PWM_Analyse(void)
{
    uint8_t duty = 0xFF;
    uint16_t pwm_H = 0;
    uint16_t pwm_L = 0;

    if (PWM_IN())   //初始为高电平,则开始等待低电平
    {
        TH1 = 0;
        while (PWM_IN()) //等待下降沿
        {
            if (TH1 >= T1_TIMEOUT)  //下降沿没有到来,判定为100%占空比
            {
                duty = 100;
                return duty;
            }
        }

        TH1 = 0;
        TL1 = 0;
        while (!PWM_IN()) //等待上升沿
        {
            if (TH1 >= T1_TIMEOUT)  //上升沿没有到来,判定为0%占空比
            {
                duty = 0;
                return duty;
            }
        }
        pwm_L = (TH1 << 8) | TL1;

        TH1 = 0;
        TL1 = 0;
        while (PWM_IN()) //等待下降沿
        {
            if (TH1 >= T1_TIMEOUT)  //下降沿没有到来,判定为100%占空比
            {
                duty = 100;
                return duty;
            }
        }
        pwm_H = (TH1 << 8) | TL1;

        duty = pwm_H * 100 / (pwm_H + pwm_L);
        return duty;
    }
    else    //当前为低电平,则开始等待高电平
    {
        TH1 = 0;
        while (!PWM_IN()) //等待上升沿
        {
            if (TH1 >= T1_TIMEOUT)  //上升沿没有到来,判定为0%占空比
            {
                duty = 0;
                return duty;
            }
        }

        TH1 = 0;
        TL1 = 0;
        while (PWM_IN()) //等待下降沿
        {
            if (TH1 >= T1_TIMEOUT)  //下降沿没有到来,判定为100%占空比
            {
                duty = 100;
                return duty;
            }
        }
        pwm_H = (TH1 << 8) | TL1;

        TH1 = 0;
        TL1 = 0;
        while (!PWM_IN()) //等待上升沿
        {
            if (TH1 >= T1_TIMEOUT)  //上升沿没有到来,判定为0%占空比
            {
                duty = 0;
                return duty;
            }
        }
        pwm_L = (TH1 << 8) | TL1;

        duty = pwm_H * 100 / (pwm_H + pwm_L);
        return duty;
    }

    return 0xFF;
}

2. 中断方式

中断方式的PWM采集原理与阻塞方式相同,只是将判定移动至外部中断中。开启MCU端口的外部中断(上升沿和下降沿中断);如果MCU外部中断触发不支持上升和下降沿中断,则先开启上升沿中断,在中断处理中切换中断触发条件。

处理方法:在中断处理函数中,根据当前电平状态,记录下定时器的值,并清零定时器的值,重新开始下一轮计时。

0%和100%的处理:设定一个定时递增的变量,同时在外部中断中执行清零操作。若该变量超过一定值(说明外部中断有较长时间没有触发),则判定为0%或100%。

uint16_t pwm_H = 0;
uint16_t pwm_L = 0;
uint16_t pwm_time_out = 0;
void EXT1_ISR(void) interrupt EXTI1_VECTOR
{
    if (PWM_IN())
    {
        pwm_L = (TH1 << 8) | TL1;    //记录低电平时间
        TH1 = 0;
        TL1 = 0;
    }
    else
    {
        pwm_H = (TH1 << 8) | TL1;    //记录高电平时间
        TH1 = 0;
        TL1 = 0;
    }

    //该变量定时递增(如1ms递增1),在外部中断中清零
    //在主程序中判断,超过一定值时认为PWM占空比为0%或100%
    pwm_time_out = 0;

    return;
}

注:使用中断方式,则占空比计算不建议放在中断中处理;同时,为了保证占空比的准确性,可以连续2-3次计算结果一致时,再确定当前占空比的结果。

3. MCU捕获方式

采用捕获方式的前提是MCU支持捕获功能。当前部分厂家推出的51内核单片机,会包含一个定时器2,其拥有捕获功能;或者采用32位单片机,一般都带有捕获功能。捕获的原理很简单,当上升沿或下降沿来临时,MCU硬件将定时器/计数器的值保存在一个影子寄存器中,并产生捕获中断。
通过固定每次上升/下降沿的计数器值,相减即可分别得出高电平值和低电平值,从而计算出占空比。
pwm占空比捕获原理
下面以某颗51内核的MCU为例,提供示例代码:

unsigned int pwm_fall = 0, pwm_rise = 0;

volatile unsigned int pwm_H;
volatile unsigned int pwm_L;

volatile unsigned char pwm_time_out;
//------------------------------------------------------------
void T2_interrupt(void) interrupt 5          //定时器2中断;
{

    if (CCCON & 0x02) //CC1中断标志位
    {
        CCCON  &= 0xFD; //清除中断标志

        if (PWM_IN())   //上升沿触发
        {
            pwm_rise = CC1;     //获取捕获寄存器中的值
            pwm_L = pwm_rise - pwm_fall;
        }
        else
        {
            pwm_fall = CC1;     //获取捕获寄存器中的值
            pwm_H = pwm_fall - pwm_rise;
        }

        //该变量定时递增(如1ms递增1),在外部中断中清零
        //在主程序中判断,超过一定值时认为PWM占空比为0%或100%
        pwm_time_out = 0;
    }
}

注: pwm_rise/pwm_fall/pwm_L/pwm_H都必须使用无符号数,否则相减时可能得到错误的值。

总结

方式一任何单片机都可以实现,但是阻塞方式会使系统的实时性变差;
方式二在使用时,需要保证外部中断的最高优先级,不可以被其他中断打断,以保证其准确性;
方式三的稳定性和准确性都较高,但是需要MCU硬件支持。

  • 37
    点赞
  • 205
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是51单片机测量PWM信号占空比和频率的代码: ``` #include <reg51.h> #include <stdio.h> #define FOSC 11059200L // 这里的FOSC需要根据实际晶振频率进行修改 #define T1MS (65536-FOSC/1000) // 定时器1每1ms中断一次 unsigned int high_time = 0, low_time = 0; // 用于保存PWM信号的高电平时间和低电平时间 unsigned int freq = 0, duty = 0; // 用于保存PWM信号的频率和占空比 void Timer1_Init(void) // 定时器1初始化函数 { TMOD |= 0x10; // 设置定时器1为模式1 TH1 = T1MS/256; // 计算定时器初值 TL1 = T1MS%256; ET1 = 1; // 允许定时器1中断 TR1 = 1; // 启动定时器1 EA = 1; // 开启总中断 } void Timer1_Isr(void) interrupt 3 // 定时器1中断处理函数 { static unsigned char count = 0; // 用于计数50次定时器中断,即50ms static bit status = 0; // 用于判断当前是高电平还是低电平 static unsigned int total_time = 0; // 用于保存PWM信号的总时间 count++; // 计时器计数器加1 total_time++; // PWM信号总时间加1 if(status == 0) // 当前为低电平 { if(!P1_3) // 检测到高电平 { high_time++; // 高电平时间加1 status = 1; // 切换到高电平状态 } } else // 当前为高电平 { if(P1_3) // 检测到低电平 { low_time++; // 低电平时间加1 status = 0; // 切换到低电平状态 } } if(count == 50) // 如果计时器计数器达到50次,即50ms { count = 0; // 计时器计数器清零 total_time = high_time + low_time; // 计算PWM信号总时间 freq = FOSC/total_time; // 计算PWM信号频率 duty = high_time*100/total_time; // 计算PWM信号占空比 high_time = 0; // 重置高电平时间 low_time = 0; // 重置低电平时间 } } void main() { Timer1_Init(); // 初始化定时器1 while(1) { printf("PWM frequency: %d Hz, duty cycle: %d%%\n", freq, duty); // 打印PWM信号的频率和占空比 } } ``` 在上述代码中,我们使用了定时器1来测量PWM信号的高电平时间和低电平时间,并根据这两个时间计算出PWM信号的频率和占空比。在每50个定时器1中断中,我们会计算一次PWM信号的频率和占空比,并将结果打印输出。需要注意的是,我们这里假设了PWM信号的周期在50ms内,如果PWM信号的周期超过了50ms,则需要对代码进行相应的修改。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值