51单片机IO口模拟PWM,问题看了一晚上找不到原因
PWM周期:20ms,也就是50Hz频率由于用于控制舵机,高电平是0.5ms~2.5ms,所以打算把20ms分为200份,没份时间就是20ms/200=0.1ms,这样5份高电平就代表0.5ms了
单片机是常用的STC89C52RC,烧录程序后从示波器看波形却傻眼了....波形上面频率是170Hz左右,看了一晚上的程序居然没看出问题。所谓旁观者清,各位来指点指点吧。
程序如下,一看就懂
/*********************************************************************************
====舵机驱动程序=========
编写:小刘
晶振:12MHz
舵机型号:MG996
舵机周期驱动信号周期:20ms
电压:4.8-7.2V
**********************************************************************************/
#include<reg52.h> //包含特殊功能寄存器的定义
sbit p11=P1^1;//输出pwm脉冲
#define uchar unsigned char
#define uint unsigned int
/* 函数&变量申明 -----------------------------------------------*/
void Init_Timer0(void); //定时器定时20ms,分为200份,每份时间0.1ms=100us
void Init_Uart() ;
uchar receive;
uchar pwm=25;//阀值,25就是2.5ms 的高电平时间
uchar period=200;//舵机信号周期20ms,分成200份
uchar i=0;//用于计数
bit temp=0;
/*
********************************************************************************
** 函数名称 : main(void)
** 函数功能 : 主函数
********************************************************************************
*/
main()
{
Init_Timer0();
Init_Uart() ;
while(1);
}
/*
********************************************************************************
** 函数名称 : Init_Timer0(void)
** 函数功能 : 定时器初始化子程序
********************************************************************************
*/
void Init_Timer0(void) //定时器初始化子程序
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
//给定初值,每份定时时间0.1ms,一个周期200份=20ms
TH0=(65535-100)/256;//0XFF
TL0=(65535-100)%256;//0X9C
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
/*
********************************************************************************
** 函数名称 : void Init_Uart()
** 函数功能 : 串口初始化子程序
********************************************************************************
*/
void Init_Uart()
{
TMOD=0x20; //用定时器设置串口波特率
TH1=0xfd;
TL1=0xfd;
TR1=1;
REN=1; //串口初始化
SM0=0;
SM1=1;
EA=1; //开启总中断
ES=1; //开串口中断
}
/*
********************************************************************************
** 函数名称 : Timer0_isr(void) interrupt 1 using 1
** 函数功能 : 定时器中断程序
********************************************************************************
*/
void Timer0_isr(void) interrupt 1
{
TH0=(65535-100)/256;
TL0=(65535-100)%256;
i++;
if (i>=pwm)p11=0; //如果超过了阀值就是电平
if (i==200){i=0;p11=1;} //0.1ms*200=20ms 周期时间到,默认给输出高电平
}
/*
********************************************************************************
** 函数名称 : void Uart()
** 函数功能 : 串口接收中断程序
********************************************************************************
*/
void Uart() interrupt 4 //串口中断响应程序
{
ES=0;//关闭串口中断
if(RI)
{
RI=0;//清除接收中断标志位
receive=SBUF;//读取SBUF
}
ES=1;//打开串口中断
} 你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时器中断了。 你的程序是2.5ms的高电平,17.5ms的低电平?周期50hz? qqliyunpeng 发表于 2014-5-18 00:33
你的程序是2.5ms的高电平,17.5ms的低电平?周期50hz?
对啊,周期就是17.5+2.5=20ms,频率就是50Hz了啊,光是这样的都没法达到预期... hithms 发表于 2014-5-18 00:19
你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时 ...
嗯嗯,这个得明天再去实验室看看了。光从程序上您能看出明显的问题吗? hithms 发表于 2014-5-18 00:19
你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时 ...
问题已找到,我的TMOD在初始化串口定时器1的时候用了“=”而不是“|=“,结果可想而知。但是新的问题出现,我现在的频率是40Hz左右...用 TH0=(65535-100)/256;TL0=(65535-100)%256; 误差如此之大? 万分惊奇,程序中使用 TH0=(65535-78)/256;
TL0=(65535-78)%256;
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 万分惊奇,程序中使用 TH0=(65535-78)/256;
TL0=(65535-78)%256;
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 tim4146 发表于 2014-5-18 15:25
万分惊奇,程序中使用
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 ...
是65535还是65536? qq开始学单片机 发表于 2014-5-19 01:05
是65535还是65536?
都是(65535-X)/256这种计算啊,即使相差1 也不至于100设定成78才达到效果吧... 本帖最后由 takashiki 于 2014-5-19 13:28 编辑
tim4146 发表于 2014-5-18 15:25
万分惊奇,程序中使用
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 ...必须的,你要知道,51很慢,尤其对于你这个应用来说,100个周期就要计时一次,进出中断的的时间无可忽略,而且赋值也是需要时间的,你需要进行补偿。
最精确的解决方案:将T0或T1工作于8位自动重载模式,中断中无需重新赋值,精确多了。
补充一下说明:
另外,你的UART中断写得过于复杂,浪费了很多CPU,这样就行了,嘿嘿:void Uart() interrupt 4 { //串口中断响应程序
if(_testbit_(RI)) //清除接收中断标志位
receive=SBUF; //读取SBUF
} 不是自装载的,在定时器里更新定时器计数值,要考虑指令消耗 换 stc12c 有记录硬件pwm输出,这样方便点{:shocked:} 不知道 STC PWM 死区怎么设定? wkman 发表于 2014-5-19 14:58
换 stc12c 有记录硬件pwm输出,这样方便点
够用的情况下我一直用STC89C52的,比较简单,后来试着用了STM32. 现在的STC12C价格怎么样? You are very challenged.
TH0 = -100 >> 8;//TH0=(65535-100)/256;//0XFF
TL0 = -100; //TL0=(65535-100)%256;//0X9C
void Timer0_isr(void) interrupt 1
{
TH0=(65535-100)/256;
//TL0=(65535-100)%256;
TL0 += -100;
void Uart() interrupt 4 //串口中断响应程序
{
ES=0;//关闭串口中断
Speechless.
Just think about it.
millwood0 发表于 2014-5-25 23:44
You are very challenged.
It seems to be a good idea,use" TL0 += -100;" to offset the time spent before.
Thank u ,u r so helpful~
页:
[1]