一个TIMER软件模拟输出6个PWM

文章讲述了在51单片机硬件资源有限的情况下,如何通过软件模拟实现6通道PWM输出。对比了两种方法:方法一是使用高频率中断,但对51单片机压力大;方法二是采用分时策略,每秒只需600次中断,降低了处理器负担。并提出了进一步优化中断次数的策略,通过排序减少中断次数。
摘要由CSDN通过智能技术生成

51单片机的timer很少,往往16bit的timer只有一个,可以硬件输出的channel也不多。如我手上这就是这个情况,timer1, 16bit, 只有三个通道。我想输出6个pwm,这个就无法硬件的方式了。只能使用软件模拟了。

方法1.

pwm是1-20ms一个周期,要是设置一个固定的周期,中断间隔就必须比较小,如0.01ms , 这个精度已经不怎么样了,但是每秒还是需要100000次中断,对于51来说,这个压力很大,实测效果很差。

方法2 我们使用分时办法来进行。

      1. pwm需要每20ms对pin脚操作两次,周期开始设置它为1,到达指定时间时设置它为0,这个宽度,在航模的信号里,是1-2ms之间。我们将6个通道,每个分时3ms给它做这第一项的两次设置。 

       2. 每20ms一周期,在一个周期里有12个状态. 分别是 CH_N_ON和CH_N_OFF.   n 取值1-6

       

   分时如上图,这样的话,我们只需要中断600次一秒就可以输出6个通道的PWM了

代码:


void et2()interrupt 12{                                //TIMER 2
		

#if DEBUG

#else	
	u16 tmp;
        TR2_CLR
       //
       //0: 0-   3-                           20
       //1    C1-3 
       //2:      3-   6-                      20  -23
       //3         C2 -6
       //4:           6-  9 -                 20     -26
       //5             C3-9
       //6:               9-   12 -           20         -29
       //7                 C4-12
       //8:                    12 -  15       20              -32
       //9                        C5-15
       //10:                          15- 18  20                  -35
       //11                            C6-18  
//T2H = T2L=0;

        switch(g_pwm_out_state)
        {
                case 0: 
											
                        CH1 = 1; 
                        tmp = g_pwm[0];
                        break;
                case 1:
									
                        CH1 = 0;
                        tmp = 3000-g_pwm[0];
                        break;
                case 2: 
                        CH2 = 1;
                        tmp = g_pwm[1];
                        break;
                case 3:
                        CH2 = 0;
                        tmp = 3000-g_pwm[1];
                        break;
                case 4:
                        CH3 = 1;
                        tmp = g_pwm[2];
                        break;
                case 5:
                        CH3 = 0;
                        tmp = 3000-g_pwm[2];
                        break;
                case 6: 
                        CH4 = 1;
                        tmp = g_pwm[3];
                        break;
                case 7:
                        CH4= 0;
                        tmp = 3000-g_pwm[3];
                        break;
                case 8:
                        CH5 = 1;
                        tmp = g_pwm[4];
                        break;
                case 9:
                        CH5= 0;
                        tmp = 7928-g_pwm[4];
                        break;
        }
        
        tmp = 0xffff - tmp*2;
        T2H =  tmp>>8;
        T2L =  tmp&0xff;
        
       
      
				if(g_pwm_out_state==11)
						g_pwm_out_state = 0;
				else 
					  g_pwm_out_state++;
				
        TR2_SET;
#endif				
				

}

   方法3. 上面的中断次数还是过多,可以进一步减少中断,可以先排序,然后执行,中断最多是pwm数量的N+1, 以下是伪代码


#include <stdio.h>
#include <stdint.h>

uint16_t    pwm_ticks[7];
uint8_t     pwm_index[7];
uint16_t 	nextTicks[8];
uint8_t     is_need_update=0; 
uint8_t     state;

uint16_t pwm_to_ticks(uint16_t src)
{
	return  src;
}

void update_pwm(uint16_t * src)
{
	uint8_t ischanged = 0;
	uint16_t ticks;
	uint8_t idx1 ;
	uint8_t idx2;
	for(int i = 0;i < 7; i++)
	{
		ticks = pwm_to_ticks(src[i]);
		if(pwm_ticks[i]!=ticks)
		{
			pwm_ticks[i] = ticks;
			ischanged = 1;
		}

	}

	if(ischanged)
	{
		for(int i=0;i<7;i++)
		{
			pwm_index[i] = i;
		}

		for(int i=0;i<6;i++)
		{
			for(int j=0;j<6-i;j++)
			{
				idx1  = pwm_index[j];
				idx2  = pwm_index[j+1];
				if(pwm_ticks[idx1] > pwm_ticks[idx2])
				{
					pwm_index[j]  = idx2;
					pwm_index[j+1] = idx1;
				}
			}
		}

		idx1  = pwm_index[0];
		nextTicks[0]  = pwm_ticks[ idx1 ];
		for(int i=1;i<7;i++)
		{
			idx1  = pwm_index[i];
			idx2  = pwm_index[i-1];
			ticks = pwm_ticks[ idx1 ] -  pwm_ticks[ idx2 ];
			nextTicks[i]= ticks; 
		}
		
		nextTicks[7]= pwm_to_ticks(20000) -pwm_ticks[pwm_index[6]] ;
		state = 0;
	}


}


void timer_int()
{
  
   for(int i=0;i<8;i++)
   {
	   if(state == 7)
	   {
	   		printf("-----set all pins----------\n");
	   		state  = 0;
	   }
	   else
	   {
	   		printf("reset pin %d\n", pwm_index[state]);
	   		 state ++ ;

	   }

	   uint16_t tt = nextTicks[state];
	 

	   if(tt > 10)
	   {
	   	    printf("set tick %u\n", tt);
	   	 	break;
	   }
	   else 
	   {	
	   	 	printf("too small %u , continue \n", tt);
	   		continue;
	   }
	}


  
}


 int main(int argc, char const *argv[])
{
	uint16_t pwms[][7] =
	{

	 {1500,1000,1600,1799,2300,1500,1500},
	 {1500,1000,1600,1400,2300,1500,1000},
	 {1000,1000,1600,1300,2300,1500,2000},
	 {1000,1250,1600,1799,2300,1500,1000},

	};

	for(int k =0;k< 4;k++)
	{


		printf("########### test round #########\n");
		update_pwm(pwms[k]);
		state = 7;
		for(int i= 0;i < 20 ;i ++)
		{	
			printf("********interrupt %u***********\n", i);
			timer_int();
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值