定时器模拟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;
		//}
	//}
//}
//

 

 

 

 

  • 10
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值