05【基础学习】定时器生成PWM波

1、蜂鸣器的使用

实验:通过定时器生成不同的波形,让蜂鸣器发出叮咚的声音

在700Hz的电平信号下,蜂鸣器发出叮的声响,在500Hz的电平信号下,蜂鸣器发出咚的声响
700Hz的电平信号的周期T ≈ 1.42ms,500Hz的电平信号周期T = 2ms

在这里插入图片描述
综上:需要产生700Hz的电平信号,即每隔0.71ms电平翻转一次即可。需要产生500Hz的电平信号,即每隔1ms电平翻转一次即可。

将定时器T0的定时为100us,则定时到了10次后让电平翻转,则就产生了周期为2ms的电平
①Timer.c文件的代码如下:

#include "Timer.h"

sbit GPIO1_0 = P1^0;

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0_Intrrupt_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值 */
//	TH0 = ((65536 - 100) >> 8);
//	TL0 = (65536 - 100) & 0x00FF;	//定时100us
//	TH0 = ((65536 - 250) >> 8);
//	TL0 = (65536 - 250) & 0x00FF;	//定时250us
	TH0 = ((65536 - 500) >> 8);
	TL0 = (65536 - 500) & 0x00FF;	//定时500us
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/********中断服务函数*******/
/**
 * T0的中断服务函数
 */
static unsigned int Period2ms = 0;
void Time0_Rountine(void) interrupt 1
{	
	//这里不用清除标志位,硬件会自动清除
//	TH0 = ((65536 - 100) >> 8);
//	TL0 = (65536 - 100) & 0x00FF;	//定时100us
//	TH0 = ((65536 - 250) >> 8);
//	TL0 = (65536 - 250) & 0x00FF;	//定时250us
	TH0 = ((65536 - 500) >> 8);
	TL0 = (65536 - 500) & 0x00FF;	//定时500us
	
	Period2ms++;				//定时时间到执行+1
	if(Period2ms == 2)			//定时1ms到了
	{
		GPIO1_0 = ~GPIO1_0;		//让电平翻转
		Count10 = 0;
	}
}

②main.c文件的代码如下:

#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	while(1)
	{

	}
}

在这里插入图片描述综上:定时周期越小,误差就越大

同理:将定时器T0的定时为100us,定时到了7.1次后让电平翻转,则就产生周期为1.42ms的电平

实验:通过定时器生成不同700Hz和500Hz的电平信号,信号都分别持续5ms
①Timer.c文件的代码如下:

#include "Timer.h"

sbit GPIO1_0 = P1^0;

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0_Intrrupt_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时100us */
	TH0 = ((65536 - 100) >> 8);
	TL0 = (65536 - 100) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/********中断服务函数*******/
/**
 * T0的中断服务函数
 */
static unsigned int Period = 0;
static unsigned int Count = 0;
static unsigned int Flag = 0;
void Time0_Rountine(void) interrupt 1
{	
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 100) >> 8);
	TL0 = (65536 - 100) & 0x00FF;
	
	Count++;
	Period++;			//每隔0.1ms执行+1
	
	if(Count == 50)		//5ms到了
	{
		Count = 0;
		Flag = !Flag;	//改变状态
	}
	switch(Flag)
	{
		case 0:							//
			if(Period == 10)			//定时1ms到了
			{
				GPIO1_0 = ~GPIO1_0;		//让电平翻转
				Period = 0;
			}
			break;
		case 1:
			if(Period == 7)				//定时0.7ms到了
			{
				GPIO1_0 = ~GPIO1_0;		//让电平翻转
				Period = 0;
			}
			break;
		default:break;
	}	
}

②main.c文件的代码如下:

#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	while(1)
	{

	}
}

在这里插入图片描述综上:使用51单片机的定时器生成不同频率的波形存在太大的误差

2、PWM波形

下图是一张典型的pwm波形图,信号从高电平>>低电平>>高电平,称为一个脉冲周期T。
在这里插入图片描述

而1s内经历的周期数称为频率 f(HZ),f=1/T。例如周期为0.02s,频率就是50HZ

2.1、占空比

    一个脉冲周期内高电平维持时间在整个周期中所占的比值

    例如:一个脉冲周期为10ms的pwm波形图,第一个周期高电平为7ms(占空比70%);第二个周期高电平为4ms(占空比40%)

在这里插入图片描述

2.2、平均电压

假设高电平为5V,根据"面积等效原理
第一个周期平均电压为5V×70%=3.5V
第二个周期平均电压为5V×40%=2V

在这里插入图片描述

2.3、实验

实验:
使用定时器T0生成一个频率为50Hz,占空比为70%的信号。

频率为50Hz,则周期T = 20ms,而占空比为70%,则信号的高电平持续时间t = 14ms,信号的低电平持续时间t = 6ms。
将定时器T0的周期时间设置为1ms即可
在这里插入图片描述

①Timer.c文件的代码如下:

#include "Timer.h"

sbit GPIO1_0 = P1^0;

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0_Intrrupt_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;

	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/********中断服务函数*******/
/**
 * T0的中断服务函数
 */
static unsigned long TimeCount = 0;
static unsigned int PWM_Cnt = 0;
void Time0_Rountine(void) interrupt 1
{	
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	TimeCount++;
	/* 生成波形 */
	PWM_Cnt++;			//每隔1ms执行+1
	if(PWM_Cnt == 20)	//20ms到了
	{
		PWM_Cnt = 0;
	}
	if(PWM_Cnt < 14)	//14ms还没有到
	{
		GPIO1_0 = 1;	//输出高电平
	}
	if(PWM_Cnt >= 14)	//14ms到了
	{
		GPIO1_0 = 0;	//变为低电平
	}
}

/**
 * 对外暴露TimeCount的值
 */
unsigned long Get_TimeCount(void)
{
	return TimeCount;
}

②main.c文件的代码如下:

#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	while(1)
	{

	}
}

在这里插入图片描述

改进代码如下:
①Timer.c文件的代码如下:

#include "Timer.h"

/**
 * 定时器T0的初始化
 */
void Time0_Intrrupt_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65535 - (1000 - 1)) >> 8);
	TL0 = (65535 - (1000 - 1)) & 0x00FF;
	
	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;				//启动定时器0
	
	/* 开启中断 */
	PT0 = 1;				//优先级较高
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}


/********中断服务函数*******/
/**
 * T0的中断服务函数(每隔1ms对TimeCount加1)
 */
static unsigned long TimeCount = 0;
static unsigned int PWM_Cnt = 0;
void Time0_Rountine(void) interrupt 1
{
	//这里不用清除标志位,硬件会自动清除
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65535 - (1000 - 1)) >> 8);
	TL0 = (65535 - (1000 - 1)) & 0x00FF;
	
	TimeCount++;
	
	/* 生成波形 */
	PWM_Cnt++;			//每隔1ms执行+1
	if(PWM_Cnt == Period)//周期到了
	{
		PWM_Cnt = 0;
	}
	if(PWM_Cnt < (Period * PWM_Duty)/100)
	{
		GPIO1_0 = 1;	//输出高电平
	}
	if(PWM_Cnt >= (Period * PWM_Duty)/100)	
	{
		GPIO1_0 = 0;	//变为低电平
	}

}

/**
 * 对外暴露TimeCount的值
 */
unsigned long Get_TimeCount(void)
{
	return TimeCount;
}

②Timer.h文件的代码如下:

#ifndef __Timer_H
#define __Timer_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

//使用宏定义占空比,方便修改
/* 宏定义周期T = 20ms*/
#define Period 20
/* 占空比70% */
#define PWM_Duty 70

sbit GPIO1_0 = P1^0;

void Time0_Intrrupt_Init(void);
unsigned long Get_TimeCount(void);

#endif

③main.c文件的代码如下:

#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	while(1)
	{

	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值