标题:
51单片机PWM输出,定时器定时不准问题?
[打印本页]
作者:
星水天河
时间:
2020-5-17 08:01
标题:
51单片机PWM输出,定时器定时不准问题?
我编写了一个PWM输出的函数,根据传进来的参数不同来选择使用定时器0或者定时器1,对P1口的某一个引脚输出PWM。并且可以根据入口参数实现不同的占空比。设计的PWM波周期为10ms,
定时器每100us中断一次(TH0=0xff;TL0=0x9c;) 每个周期包含100个中断。但用Keil的调试工具逻辑分析仪进行测试时发现,实测PWM波周期为80ms。即使考虑了中断调用和其他语句的执行所耗费的时间也不应该差这么多倍吧?是不是程序有问题?麻烦各位大佬帮忙看看程序吧。工程由pwm.h,pwm.c和main.c组成。
main.c中程序为
#include "reg52.h"
#include "pwm.h"
void main()
{
pwm(P1_0,30,Timer0);
}
pwm.h和pwm.c
作者:
wulin
时间:
2020-5-17 08:01
不存在定时器不准,而是使用不当。
#include <reg51.h>
sbit PWM=P1^0;
unsigned char i;
unsigned char j=50;//0~100%占空比
void InitTimer0(void)//100us
{
TMOD = 0x02;//自动重装
TH0 = 0x9C;
TL0 = 0x9C;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void main(void)
{
InitTimer0();
while(1);
}
void Timer0Interrupt() interrupt 1
{
if(i<j)
PWM=1;
else PWM=0;
i++;
i%=100;
}
作者:
muzhi
时间:
2020-5-17 09:36
似乎文件没上传上来?
作者:
星水天河
时间:
2020-5-17 12:29
pwm.h的代码如下:
#ifndef __PWM__
#define __PWM__
#define Timer0 0x8A //Timer 0的低字节
#define Timer1 0x8B //Timer 1的低字节
#define P1_0 0x01
#define P1_1 0x02
#define P1_2 0x04
#define P1_3 0x08
#define P1_4 0x10
#define P1_5 0x20
#define P1_6 0x40
#define P1_7 0x80
//对PWM函数参数判断
#define IS_P1(Pin) ( ((Pin)==P1_0)|| \
((Pin)==P1_1)|| \
((Pin)==P1_2)|| \
((Pin)==P1_3)|| \
((Pin)==P1_4)|| \
((Pin)==P1_5)|| \
((Pin)==P1_6)|| \
((Pin)==P1_7) )
#define IS_Timer(Timer) ( ((Timer)==Timer0)|| \
((Timer)==Timer1) )
typedef unsigned char u8;
typedef int u16;
void pwm(u8 Pin,u8 Duty_cycle,u8 Timer);
#endif
pwm.c的代码如下:
#include "pwm.h"
#include "reg52.h"
u8 g_Pin;
u8 g_Duty_cycle;
u8 g_Voltage;
u8 g_Count; //用于中断计数,控制占空比
//输出周期为10ms,占空比可调的PWM波
//@param Pin:指定输出PWM波的引脚,支持P1口的8个引脚,格式:P1_X
//@param: Duty_cycle:指定占空比,应为从1-99的整数
//@param: Timer:指定使用Timer0或Timer1进行定时
//@retval None
void pwm(u8 Pin,u8 Duty_cycle,u8 Timer)
{
//该函数开启相应的定时计数器,完成相关初始化工作,最后由中断服务函数完成PWM功能
if(!IS_P1(Pin))
return;
if(!IS_Timer(Timer))
return;
if(Duty_cycle>99 || Duty_cycle<1)
return;
g_Pin=Pin;
g_Duty_cycle=Duty_cycle;
if(Timer==Timer0)
{
//完成定时器的初始化
TH0=0xff;
TL0=0x9c; //设置初值为65436,每100us产生一次中断
//启动Timer0及相应中断
IE|=0x82; //允许Timer0中断
TMOD|=0x01;
TCON|=0x10; //开启Timer0
}
else
{
//完成定时器的初始化
TH1=0xff;
TL1=0x9c; //设置初值为65436,每100us产生一次中断
//启动Timer1及相应中断
IE|=0x88; //允许Timer1中断
TMOD|=0x10;
TCON|=0x40; //开启Timer1
}
g_Voltage=1;
P1|=Pin;//将相应IO口设为高电平
g_Count=Duty_cycle;
}
void timer0() interrupt 1
{
TH0=0xff;
TL0=0x9c; //重新赋计数初值
g_Count--;
if(!g_Count)//计数器计数至0说明电平需要发生翻转
{
if(g_Voltage)//原来为高电平,翻转为低电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=0;
g_Count=100-g_Duty_cycle;
}
else//原来为低电平,再次翻转为高电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=1;
g_Count=g_Duty_cycle;
}
}
}
void timer1() interrupt 3
{
TH1=0xff;
TL1=0x9c; //重新赋计数初值
g_Count--;
if(!g_Count)//计数器计数至0说明电平需要发生翻转
{
if(g_Voltage)//原来为高电平,翻转为低电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=0;
g_Count=99-g_Duty_cycle;
}
else//原来为低电平,再次翻转为高电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=1;
g_Count=g_Duty_cycle;
}
}
}
作者:
星水天河
时间:
2020-5-17 12:44
pwm.h文件
#ifndef __PWM__
#define __PWM__
#define Timer0 0x8A //Timer 0的低字节
#define Timer1 0x8B //Timer 1的低字节
#define P1_0 0x01
#define P1_1 0x02
#define P1_2 0x04
#define P1_3 0x08
#define P1_4 0x10
#define P1_5 0x20
#define P1_6 0x40
#define P1_7 0x80
//对PWM函数参数判断
#define IS_P1(Pin) ( ((Pin)==P1_0)|| \
((Pin)==P1_1)|| \
((Pin)==P1_2)|| \
((Pin)==P1_3)|| \
((Pin)==P1_4)|| \
((Pin)==P1_5)|| \
((Pin)==P1_6)|| \
((Pin)==P1_7) )
#define IS_Timer(Timer) ( ((Timer)==Timer0)|| \
((Timer)==Timer1) )
typedef unsigned char u8;
typedef int u16;
void pwm(u8 Pin,u8 Duty_cycle,u8 Timer);
#endif
pwm.c文件
#include "pwm.h"
#include "reg52.h"
u8 g_Pin;
u8 g_Duty_cycle;
u8 g_Voltage;
u8 g_Count; //用于中断计数,控制占空比
//输出周期为10ms,占空比可调的PWM波
//@param Pin:指定输出PWM波的引脚,支持P1口的8个引脚,格式:P1_X
//@param: Duty_cycle:指定占空比,应为从1-99的整数
//@param: Timer:指定使用Timer0或Timer1进行定时
//@retval None
void pwm(u8 Pin,u8 Duty_cycle,u8 Timer)
{
//该函数开启相应的定时计数器,完成相关初始化工作,最后由中断服务函数完成PWM功能
if(!IS_P1(Pin))
return;
if(!IS_Timer(Timer))
return;
if(Duty_cycle>99 || Duty_cycle<1)
return;
g_Pin=Pin;
g_Duty_cycle=Duty_cycle;
if(Timer==Timer0)
{
//完成定时器的初始化
TH0=0xff;
TL0=0x9c; //设置初值为65436,每100us产生一次中断
//启动Timer0及相应中断
IE|=0x82; //允许Timer0中断
TMOD|=0x01;
TCON|=0x10; //开启Timer0
}
else
{
//完成定时器的初始化
TH1=0xff;
TL1=0x9c; //设置初值为65436,每100us产生一次中断
//启动Timer1及相应中断
IE|=0x88; //允许Timer1中断
TMOD|=0x10;
TCON|=0x40; //开启Timer1
}
g_Voltage=1;
P1|=Pin;//将相应IO口设为高电平
g_Count=Duty_cycle;
}
void timer0() interrupt 1
{
TH0=0xff;
TL0=0x9c; //重新赋计数初值
g_Count--;
if(!g_Count)//计数器计数至0说明电平需要发生翻转
{
if(g_Voltage)//原来为高电平,翻转为低电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=0;
g_Count=100-g_Duty_cycle;
}
else//原来为低电平,再次翻转为高电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=1;
g_Count=g_Duty_cycle;
}
}
}
void timer1() interrupt 3
{
TH1=0xff;
TL1=0x9c; //重新赋计数初值
g_Count--;
if(!g_Count)//计数器计数至0说明电平需要发生翻转
{
if(g_Voltage)//原来为高电平,翻转为低电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=0;
g_Count=99-g_Duty_cycle;
}
else//原来为低电平,再次翻转为高电平
{
P1^=g_Pin; //相应Pin翻转电平
g_Voltage=1;
g_Count=g_Duty_cycle;
}
}
}
作者:
13681878016
时间:
2020-5-18 02:54
星水天河 发表于 2020-5-17 17:12
对你说的没有错。你说100次中断造成80us的误差。但是现在问题是我设计的10ms为一个周期,实际仿真测出80m ...
没看出来。。你这个用了两个定时器生成1路pwm波,可能是定时器0会打断定时器1吧(中断优先级,我记得默认T1不能打断T0),生成中断嵌套。 其实用一个定时器就可以生成1路pwm波了。
你这个开头.h写的像stm32风格,我看懂了造了个入口检验。中断里没看懂,有点复杂了感觉(耗时长),周期那里为什么要100-duty不太理解。你看看我写的那个吧,一个定时器就够了,误差也还行。
作者:
星水天河
时间:
2020-5-18 08:52
感谢大家的帮助!问题已经解决。问题出在中断服务函数。当原来为低电平,要翻转为高电平时,应该用P1&=(~g_Pin)而不是P1^=g_Pin; 感谢上面网友给出的代码,我对现有的函数作了一些优化。能满足预期要求
作者:
天风情
时间:
2020-5-18 09:23
现在增强型51一般都有硬件PWM输出
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1