定时器模拟PWM输出(三款代码介绍)

1.简介
         PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写。它是通过对一系列脉冲的宽度进行调制,等效输出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化。

         占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%。

        简单的说:假设在1毫秒,0.5毫秒开,0.5毫秒灭,那么灯的闪烁频率就很高。闪烁频率超过一定值,人眼就会感觉不到。所以只能看到灯的亮度只有原来的一半。这时PWM占空比为50%,PWM周期为1ms,PWM频率F=1/1ms=1KHZ。同理,如果在1毫秒内,0.1毫秒开,0.9毫秒灭,那么,灯的亮度就只有原来的10分之一。

2.实现方式
(1)芯片自带PWM模块,设置寄存器就可以使用PWM。比如晟矽微芯片型号MC30P6280,根据数据手册就可以快速上手。

(2)利用定时器模拟PWM输出,本章重点。

3.代码分析
3.1普通版

PWM占空比为50%,PWM周期为1ms,PWM频率F=1/1ms=1KHZ。

为什么定时器要设置成70us中断一次?因为多次试验后,1/(70us*12)的实际频率最接近1Khz。根据网上设置的1/(10us*100)根本达不到1K频率。可以自己多尝试设置参数。

控制PWM开关代码最好放在定时中断函数里面。

#include "reg52.h"             //此文件中定义了单片机的一些特殊功能寄存器
 
typedef unsigned int u16;      //对数据类型进行声明定义
typedef unsigned char u8;
 
//--定义使用的IO口--//
sbit PWM=P2^1;
//--定义一个全局变量--//
u16 count=12;       //中断次数,即决定PWM周期
u16 pwm_disp=6;   //占空比显示单元,初始化为50%
/*******************************************************************************
* 函 数 名         : Timer1Init
* 函数功能           : 定时器1初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1Init()
{
    TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。
 
    TH1 = (65536-70)/256;
    TL1 = (65536-70)%256;  
    
    EA=1;//打开总中断    
    ET1=1;//打开定时器1中断允许
    TR1=1;//打开定时器            
}
 
/*******************************************************************************
* 函 数 名       : main
* 函数功能         : 主函数
* 输    入       : 无
* 输    出         : 无
*******************************************************************************/
void main()
{   
    Timer1Init();  //定时器1初始化  
    while(1)
    {          
    }        
}
 
/*******************************************************************************
* 函 数 名       : Time1
* 函数功能         : 定时器1的中断函数
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/
 
void Time1(void) interrupt 3    //3 为定时器1的中断号 
{
 
    TH1 = (65536-70)/256;          //70us
    TL1 = (65536-70)%256;
 
    count--;
 
   if(pwm_disp==count)
    {
        PWM=1;
    }
    if(count==0)
    {
           PWM=0;
          count=12;
    }   
 
}
 
   3.2汇编版

        PWMH1 DATA 30H 
        PWM   DATA 33H ;PWM周期
        COUNTER DATA 35H ;高电平脉冲的个数
        TEMP DATA 36H
 
        ORG 0000H
        AJMP MAIN
        ORG 000BH
        AJMP INTT0
        ORG 0100H
MAIN:
        MOV TMOD,#0X01  //配置T0为模式1
        MOV TH0,#HIGH(65536-100) 
        MOV TL0,#LOW(65536-100)
 
        SETB ET0 ;使能定时器0中断
        SETB EA ;使能总中断
        SETB TR0 ;开始计时
 
        MOV PWMH1,#10 ; PWM 1 占空比 可以修改
        MOV COUNTER,#0
        MOV PWM,#10 ; 
        JMP $
INTT0: 
        PUSH PSW ; 现场保护
        PUSH ACC
        
        MOV TH0,#HIGH(65536-100)
        MOV TL0,#LOW(65536-100)
 
        INC COUNTER ; 计数值加1
        MOV A,COUNTER
        CJNE A,PWMH1,INTT01 ; 如果等于高电平脉冲数
        SETB P1.0 ; P1.0变为高电平 PWM1
INTT01:
        CJNE A,PWM,INTT05 ;如果等于周期数
        MOV COUNTER,#0 ;计数器复位
        CLR P1.0 ;P1.0为低电平
 
INTT05:
        POP ACC ;出栈
        POP PSW
        RETI
 
 
        END
3.3进阶版(参考网上的)

这版代码值得学习的地方比较多,可以自己调整PWM频率和占空比,精度也较高

注意:(1)整体代码功能------用5挡开关控制PWM占空比和频率,自己可以根据情况增删代码。

          (2)注释代码功能-------是可以用定时器T1动态调整PWM的占空比和频率。

#include "reg52.h"             //此文件中定义了单片机的一些特殊功能寄存器
 
typedef unsigned int u16;      //对数据类型进行声明定义
typedef unsigned char u8;
 
//--定义使用的IO口--//
sbit PWMOUT = P0^0;  //pwm输出
sbit K1=P1^0;        //5挡开关引脚
sbit K2=P1^1;
sbit K3=P1^2;
sbit K4=P1^3;
 
//定义5挡开关状态
#define FLAG_F1  ((K1==1)&&(K2==1)&&(K3==1)&&(K4==1)) //第1档
#define FLAG_F2  ((K1==0)&&(K2==1)&&(K3==1)&&(K4==1)) //第2档
#define FLAG_F3  ((K1==1)&&(K2==0)&&(K3==1)&&(K4==1)) //第3档
#define FLAG_F4  ((K1==1)&&(K2==1)&&(K3==0)&&(K4==1)) //第4档
#define FLAG_F5  ((K1==1)&&(K2==1)&&(K3==1)&&(K4==0)) //第5档
 
 
unsigned long PeriodCnt = 0; //PWM周期计数值
unsigned char HighRH = 0; //高电平重载bai值的高字节
unsigned char HighRL = 0; //高电平重载值的低字节
unsigned char LowRH = 0; //低电平重载值的高字节
unsigned char LowRL = 0; //低电平重载值的低字节
unsigned char T1RH = 0; //T1重载值的高字节
unsigned char T1RL = 0; //T1重载值的低字节
 
void ConfigTimer1(unsigned int ms);
void ConfigPWM(unsigned int fr, unsigned char dc);
void AdjustDutyCycle(unsigned char dc);
 
 
void main()
{
    u8 num=1;   //定义开关档数
    EA = 1;     //开总中断
    
    ConfigPWM(1000, 0); //配置并启动PWM
    while(1)
    {
        
        if(FLAG_F1)
        {
            num=1;    
        }
        else if(FLAG_F2)
        {
            num=2;
        }
        else if(FLAG_F3)    
        {        
              num=3;
        }
        else if(FLAG_F4)    
        {    
              num=4;
        }
        else if(FLAG_F5)    
        {    
              num=5;
        }    
        switch(num)
        {
            case 1: AdjustDutyCycle(100);break; //频率一定,调整占空比
            case 2: AdjustDutyCycle(17);break;
            case 3: AdjustDutyCycle(50);break;
            case 4: AdjustDutyCycle(74);break;
            case 5: AdjustDutyCycle(0);break;
        }
 
    }
//    ConfigTimer1(50); //用T1定时调整占空比
//    while (1);
}
/* 配置并启动T1,ms-定时时间 */
//void ConfigTimer1(unsigned int ms)
//{
//    unsigned long tmp; //临时变量
//    
//    tmp = 12000000 / 12; //定时器计数频率
//    tmp = (tmp * ms) / 1000; //计算所需的计数值
//    tmp = 65536 - tmp; //计算定时器重载值
//    tmp = tmp + 12; //补偿中断响应延时造成的误差
//    T1RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
//    T1RL = (unsigned char)tmp;
//    TMOD &= 0x0F; //清零T1的控制位
//    TMOD |= 0x10; //配置T1为模式1
//    TH1 = T1RH; //加载T1重载值
//    TL1 = T1RL;
//    ET1 = 1; //使能T1中断
//    TR1 = 1; //启动T1
//}
/* 配置并启动PWM,fr-频率,dc-占空比 */
void ConfigPWM(unsigned int fr, unsigned char dc)
{
    unsigned int high, low;
    
    PeriodCnt = (12000000/12) / fr; //计算一个周期所需的计数值
    high = (PeriodCnt*dc) / 100; //计算高电平所需的计数值
    low = PeriodCnt - high; //计算低电平所需的计数值
    high = 65536 - high + 12; //计算高电平的定时器重载值并补偿中断延时
    low = 65536 - low + 12; //计算低电平的定时器重载值并补偿中断延时
    HighRH = (unsigned char)(high>>8); //高电平重载值拆分为高低字节
    HighRL = (unsigned char)high;
    LowRH = (unsigned char)(low>>8); //低电平重载值拆分为高低字节
    LowRL = (unsigned char)low;
    TMOD &= 0xF0; //清零T0的控制位
    TMOD |= 0x01; //配置T0为模式1
    TH0 = HighRH; //加载T0重载值
    TL0 = HighRL;
    ET0 = 1; //使能T0中断
    TR0 = 1; //启动T0
    PWMOUT = 1; //输出高电平
}
/* 占空比调整函数,频率不变只调整占空比 */
void AdjustDutyCycle(unsigned char dc)
{
    unsigned int high, low;
    
    high = (PeriodCnt*dc) / 100; //计算高电平所需的计数值
    low = PeriodCnt - high; //计算低电平所需的计数值
    high = 65536 - high + 12; //计算高电平的定时器重载值并补偿中断延时
    low = 65536 - low + 12; //计算低电平的定时器重载值并补偿中断延时
    HighRH = (unsigned char)(high>>8); //高电平重载值拆分为高低字节
    HighRL = (unsigned char)high;
    LowRH = (unsigned char)(low>>8); //低电平重载值拆分为高低字节
    LowRL = (unsigned char)low;
}
/* T0中断服务函数,产生PWM输出 */
void InterruptTimer0() interrupt 1
{
    if (PWMOUT == 1) //当前输出为高电平时,装载低电平值并输出低电平
    {
        TH0 = LowRH;
        TL0 = LowRL;
        PWMOUT = 0;
    }
    else //当前输出为低电平时,装载高电平值并输出高电平
    {
        TH0 = HighRH;
        TL0 = HighRL;
        PWMOUT = 1;
    }
}
/* T1中断服务函数,定时动态调整占空比 */
//void InterruptTimer1() interrupt 3
//{
    //static bit dir = 0;
    //static unsigned char index = 0;
    //unsigned char code table[13] = { //占空比调整表
    //5, 18, 30, 41, 51, 60, 68, 75, 81, 86, 90, 93, 95
    //};
    //
    //TH1 = T1RH; //重新加载T1重载值
    //TL1 = T1RL;
    //AdjustDutyCycle(table[index]); //调整PWM的占空比
    //if (dir == 0) //逐步增大占空比
    //{
        //index++;
        //if (index >= 12)
        //{
        //dir = 1;
        //}
    //}
    //else //逐步减小占空比
    //{
        //index--;
        //if (index == 0)
        //{
            //dir = 0;
        //}
    //}
//}
//

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值