STC单片机的一个定时器怎么在两个IO口同时输出2个可调的PWM波
我用的STC15L204EA单片机,想用一个定时器控制2个舵机,设计要求是:一个舵机在转动的时候,另外一个舵机的信号也在一直保持,也就是给2个舵机一直发送信号,并且保持住这个信号,想了好几天也不知道怎么解决,各位大侠能不能给个思路,最好说的详细些,谢谢 再补充一下,不是通过设置,而是C语言纯编程,我查过网上一个简单的示意程序:void TIMER_HANDLE(void)
{
//判断是否达到TC值,如果达到则复位(COUNT清零),否则执行匹配中断
//在原有基础上设置匹配值中断,匹配值 +=300;
COUNT++;
if((COUNT == IO1_H_TIME)) //现在正好是需要高电平的时刻,因此值置高
{
//持续时间开启(自减),是否为0?
//不是0,那么继续输出高电平
//是0,关闭输出,同时清除高电平时刻
}
}
但是讲的不是很清楚,希望各位高手能说一下思路。先谢谢了! 不知你需要多少位多少频率的PWM?
// STC 1T @ 12MHz
#include <STC15F204EA.H>
sbit PWM0 = P1^0; //PWM0输出
sbit PWM1 = P1^1; //PWM1输出
uint8 num0; //PWM0占空比
uint8 num1; //PWM1占空比
uint8 cnt; //PWM计数器
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:模拟PWM初始化(用定时器0模拟)
说明:PWM时基30us,256级,周期7680us,频率约130Hz
注意:PWM时基 >> 中断程序执行时间
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void PWM_Init_T0()
{
TMOD &= 0xF0; //清定时器0
TMOD |= 0x02; //定时器0方式2,8位自动重装模式
TH0 = TL0 = 256-30; //30us
EA= 1; //CPU中断 开
ET0 = 1; //T0中断 开
TR0 = 1;
}
void main()
{
PWM_Init_T0();
num0=1; //PWM0默认占空比1/256
num1=1; //PWM1默认占空比1/256
while(1)
{
}
void time0(void) interrupt 1
{
cnt++;
if(cnt < num0)PWM0=1; else PWM0=0;
if(cnt < num1)PWM1=1; else PWM1=0;
} 二楼的意思就是用一个定时器产生基准定时(30uS)中断,然后根据需要两路输出PWM。如果加入N个标志位以及N个计数单元,就能够控制N路的PWM输出。我是专门写汇编的,我就这么处理。呵呵 回复【2楼】BXAK
-----------------------------------------------------------------------
多谢指教.我用的晶振33MHz,是用来驱动舵机的,总周期是20ms,高电平持续时间是0.5ms,1ms,1.5ms,2ms,2.5ms,占空比是一直变化的,具体项目是:四个按键控制2个舵机左右上下转动,当控制左右的一个按键按下时,左右方向的舵机转动,上下方向的舵机虽然不动,但是其控制信号也要一直发送,当按键松开时,左右和上下的舵机都要保持它们的状态,也就是信号还是一直发送。 回复【2楼】BXAK
-----------------------------------------------------------------------
这个程序好像有点问题,cnt不可能小于num0吧,每次进入中断cnt++,也就是cnt至少得等于1。 回复【4楼】mianchaodahai
-----------------------------------------------------------------------
“总周期是20ms,高电平持续时间是0.5ms,1ms,1.5ms,2ms,2.5ms,占空比是一直变化的……”
那程序是LED调光的,只供参考,不适合你用,但原理差不多,晶振33MHz的1T STC 很容易办到的。
回复【5楼】mianchaodahai
-----------------------------------------------------------------------
uint8 num0;
cnt++到255,再加1就溢出等于0了,0至255反复循环 回复【4楼】mianchaodahai
-----------------------------------------------------------------------
“总周期是20ms,高电平持续时间是0.5ms,1ms,1.5ms,2ms,2.5ms,占空比是一直变化的……”
高电平持续时间每次递增都是0.5ms吗?最大只到2.5ms吗?
如果是,你每0.5ms中断就够了 以前看过这方面的资料,不过现在手里没有了。一个定时器可以控制八个舵机,具体方法就是,以2.5ms为一个周期,每个周期控制一个舵机。定时器开始前,令第一个舵机控制引脚保持高电平,其他为低电平,定时周期为第一个舵机的高电平时间,第一次进入中断后,控制引脚变为低电平,定时周期变为5ms-高电平时间,第二次进入中断后第二个舵机变为高电平,定时周期改为第二个舵机高电平时间,以此类推。可以控制八路舵机。 高手 2楼的程序需要频繁进中断,但只要IO口够用,CPU够快,可以挂很多舵机,而且由于占空比可以在0~1之间变化,挂的不局限于舵机。LED,电机都可以。
8楼的方式效率高,进中断的次数少,但舵机数有限。而且占空比只能在有限的范围内变化,不适合LED和电机。LZ舵机少,用8楼的方式比较适合。
最方便的还是硬件PWM。LZ可以试试ATMEGA48,ATMEGA48的16位PWM可以将20毫秒20000细分,精确到0.001毫秒,还不用进中断。
以下是4MHz晶体下将20毫秒10000细分的程序:
初始化:
void Init_PWM(void)
{
TCCR1A=_BV(COM1A1)|_BV(COM1B1)|_BV(WGM11);
TCCR1B=_BV(WGM13)|_BV(WGM12)|_BV(CS11);//8分频
ICR1=9999;//20ms分成10000份
OCR1A=750;//中间位置
OCR1B=750;
}
设置角度,如Rot(0,1600)表示将通道0的高电平时间设成1.6ms:
void Rot(uint8_t way,uint16_t ms)
{
ms>>=1;
if(way)OCR1A=ms;
else OCR1B=ms;
} static void Timer0Init(void);
static void MotorProcess(void);
static void KeyProcess(void);
sbit M1 = P1^1;
sbit M2 = P1^2;
sbit M3 = P1^3;
sbit M4 = P1^4;
static unsigned char Motor1;
static unsigned char Motor2;
static unsigned char Motor3;
static unsigned char Motor4;
volatile unsigned char Count;
void main()
{
Timer0Init();
Motor1 = 1;
Motor2 = 2;
Motor3 = 3;
Motor4 = 4;
while(1)
{
MotorProcess();
KeyProcess();
}
}
static void Timer0Init(void)
{
//6M 晶振
//定时器设为周期为0.5ms,自重载方式
TMOD = 0x2;
TH0 = 5;
TL0 = 5;
TCON = 0x50; //TR0=1,TR1=1
IE = 0x82;
}
static void MotorProcess(void)
{
if(Count < Motor1)
M1 = 1;
else
M1 = 0;
if(Count < Motor2)
M2 = 1;
else
M2 = 0;
if(Count < Motor3)
M3 = 1;
else
M3 = 0;
if(Count < Motor4)
M4 = 1;
else
M4 = 0;
}
static void KeyProcess(void)
{
//按键处理,改变Motor1-Motor4的数值,从而改变电机1-电机4的导通角
//0:恒为低电平,>=4:恒为高电平
}
void Timer0Isr(void) interrupt 1
{
Count++;
if(Count > 4)
Count = 0;
}
//刚写的程序,支持4个电机,简单调试了一下,希望能帮到你。 mark 回复【7楼】BXAK
回复【4楼】mianchaodahai
-----------------------------------------------------------------------
“总周期是20ms,高电平持续时间是0.5ms,1ms,1.5ms,2ms,2.5ms,占空比是一直变化的……”
高电平持续时间每次递增都是0.5ms吗?最大只到2.5ms吗?
如果是,你每0.5ms中断就够了
-----------------------------------------------------------------------
不是0.5ms中断,和步进电机差不多吧,每次都减一个阶梯,比如100,每次都减3,你说的这种中断好像不行,因为这种中断操作都是整数倍,而设计要求实际是100-3,最后不是大于就是小于,说的不是很清楚,就是占空比在0.5ms到1ms之间也是变化的 回复【11楼】LXM_0922
-----------------------------------------------------------------------
谢谢!
你写的这个程序好像是固定的时间中断,而设计要求是:不是0.5ms,1ms,1.5ms,2ms,2.5ms这5个值,而是0.5ms和2.5ms之间的任何值,那样舵机运行的比较平稳,就是一步一步的,比如步长为20,0.5ms+20,(0.5ms+20)+20,。。。一直加到2.5ms左右,而且2个舵机的信号也要一直保持。 回复【11楼】LXM_0922
-----------------------------------------------------------------------
例如:这个程序是错误的,但是能给一个我要表示的大致的思路
按键处理函数:case 4: //左勾头 +
{
change_time_bit=1;//启动长按时间标志
th1_bak-=STEP;
if(th1_bak<MOTO_RIGHT)//MOTO_RIGHT为2.5ms
{th1_bak=MOTO_RIGHT;gt_max_bit=0;}//gt_max_bit=0时,说明舵机已到极限值,禁止发送舵机信号
else
gt_max_bit=1;//gt_max_bit=1时,允许发送舵机信号。
change_duoji_bit=1;//切换另一个舵机
break;
}
void int_t1(void) interrupt 3//0.36us
{ //t1 1.5MS
if(moto_pwm_bit)
{
TH1=(TIME_20MS-th1_bak)>>8;
TL1=(TIME_20MS-th1_bak)>>&0XFF;
moto_en=gt_max_bit; //gt_max_bit=1时,允许发送舵机信号。
//gt_max_bit=0时,说明舵机已到极限值,禁止发送舵机信号
led_en=gt_max_bit1;//moto_en是一个舵机控制端,led_en是另一个舵机控制端
}
else
{
TH1=th1_bak>>8;
TL1=th1_bak&0xff;
moto_en=0;
led_en=0;
}
moto_pwm_bit=~moto_pwm_bit;
} mark mark 定时器1控制20ms的中断,定时0控制高电平(需要的脉宽)的时间。每次定时器1中断后打开定时器0,等定时器0定时完后的在中断内关闭定时器0。因为舵机需要的脉宽为0.5ms-1.5ms,在20ms内分四份可以控制5个舵机,可以的http://cache.amobbs.com/bbs_upload782111/files_51/ourdev_716362N2XT1A.JPG
(原文件名:PWM.JPG) 没用过舵机,抽空了解了一下。
10楼分析得很不错,用8楼的方案不错(和18楼的原理一样),你只控制2路,20ms内分2等分就够了,进入中断少提高CPU的效率,又可以实现周期20ms,0.5ms~2.5ms任意脉宽 回复【11楼】lxm_0922
-----------------------------------------------------------------------
谢谢!
你写的这个程序好像是固定的时间中断,而设计要求是:不是0.5ms,1ms,1.5ms,2ms,2.5ms这5个值,而是0.5ms和2.5ms之间的任何值,那样舵机运行的比较平稳,就是一步一步的,比如步长为20,0.5ms+20,(0.5ms+20)+20,。。。一直加到2.5ms左右,而且2个舵机的信号也要一直保持。
-----------------------------------------------------------------------
你要程序是不是一个PWM宽度是从0到2.5ms可调的?还是说,机舵有5个角度,然后在每个角度切换时,需要缓慢的变动,即细分式。如0.5ms到1ms时是0.5ms->0.6ms->0.7ms->0.8ms->0.9ms->1ms,是这样的吗? 回复【20楼】LXM_0922
-----------------------------------------------------------------------
对,是可以任意角度旋转的,就是需要缓慢的变动,而且当按键松开时,舵机停止转动,保持住信号。 回复【19楼】BXAK
-----------------------------------------------------------------------
非常感谢你的提示,我想一下先 回复【21楼】mianchaodahai
回复【20楼】lxm_0922
-----------------------------------------------------------------------
对,是可以任意角度旋转的,就是需要缓慢的变动,而且当按键松开时,舵机停止转动,保持住信号。
-----------------------------------------------------------------------
你这个跟步进电机细分很相似,你只要把我之前那段程序的定时器的定时间设小一点,然后Count的值范围变大就可以了。
点击此处下载 ourdev_716544TG8NIF.rar(文件大小:692字节) (原文件名:main.rar) 回复【22楼】mianchaodahai
-----------------------------------------------------------------------
仅供参考(已通过仿真)
// 晶振:12MHz
#include <STC15F204EA.H>
sbit PWM0 = P1^0;
sbit PWM1 = P1^1;
uint16 PWM0_K,PWM1_K; //脉宽500us ~ 2500us
uint8TL_K,TH_K;
uint8num;
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:PWM初始化
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void PWM_Init_T0()
{
TMOD &= 0xF0; //清定时器0
TMOD |= 0x01; //定时器0方式1,16位模式
TL0 = (65536-PWM0_K); //T0初始值(PWM0脉宽时间)
TH0 = (65536-PWM0_K)>>8;
EA= 1; //允许CPU中断
ET0 = 1; //允许T0中断
num= 0;
TL_K = (55536+PWM0_K); //预装定时值(65536-(10000-PWM0_K))
TH_K = (55536+PWM0_K)>>8;
PWM0 = 1; //初始化PWM0
PWM1 = 0; //初始化PWM1
TR0 = 1;
}
void main()
{
PWM0_K = 1000; //默认1ms
PWM1_K = 1000; //默认1ms
PWM_Init_T0();
while(1);
}
/*
2路PWM周期20ms,0.5ms~2.5ms任意脉宽原理:20ms分2等份,每等份10ms完成一路PWM脉宽。
*/
void time0() interrupt 1
{
TL0 = TL_K; //装载预装值
TH0 = TH_K;
switch(num)
{
case 0:PWM0 = 0;
TL_K = (65536-PWM1_K); //预装PWM1脉宽时间
TH_K = (65536-PWM1_K)>>8;
break;
case 1:PWM1 = 1;
TL_K = (55536+PWM1_K); //预装(10ms-PWM1脉宽时间)
TH_K = (55536+PWM1_K)>>8;
break;
case 2:PWM1 = 0;
TL_K = (65536-PWM0_K); //预装PWM0脉宽时间
TH_K = (65536-PWM0_K)>>8;
break;
case 3:PWM0 = 1;
TL_K = (55536+PWM0_K); //预装(10ms-PWM0脉宽时间)
TH_K = (55536+PWM0_K)>>8;
break;
}
if(++num > 3)num=0;
}
(上面的程序比下面的好)
下面这个比较直观,但对12T的51要加一定修正值,不过1T的STC 33MHz速度够快,应该不需要
//12MHz
void time0() interrupt 1
{
switch(num)
{
case 0:PWM0 = 1;
TL0 = (65536-PWM0_K);
TH0 = (65536-PWM0_K)>>8;
break;
case 1:PWM0 = 0;
TL0 = (55536+PWM0_K);
TH0 = (55536+PWM0_K)>>8;
break;
case 2:PWM1 = 1;
TL0 = (65536-PWM1_K);
TH0 = (65536-PWM1_K)>>8;
break;
case 3:PWM1 = 0;
TL0 = (55536+PWM1_K);
TH0 = (55536+PWM1_K)>>8;
break;
}
if(++num > 3)num=0;
} 非常感谢各位高手的指导,我再好好想想
页:
[1]