使用4个16位定时器进行400hz PWM

时间:2015-01-12 11:32:11

标签: c++ timer arduino pwm motordriver

我正在处理基于arduino mega的四轴飞行器并尝试为4个电机制作PWM频率 - 每个400hz。我发现了一个有趣的解决方案,其中4个ATmega2560 16位定时器用于控制4个带有PWM的ESC它可以达到400hz的频率。 ESC正在处理的正常脉冲宽度为700到2000μs。
1秒/ REFRESH_INTERVAL = 1 / 0.0025 = 400hz。

this is servo.h lib:
#define MIN_PULSE_WIDTH       700     // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH      2000     // the longest pulse sent to a servo 
#define DEFAULT_PULSE_WIDTH  1000     // default pulse width when servo is attached
#define REFRESH_INTERVAL     2500     // minimum time to refresh servos in microseconds 

#define SERVOS_PER_TIMER       1     // the maximum number of servos controlled by one timer 
#define MAX_SERVOS   (_Nbr_16timers  * SERVOS_PER_TIMER)

问题是使其工作每个PWM应该用1 16bit定时器控制。例如,在1个计时器上使用2个escs会产生200hz。因此所有16位定时器都忙于控制4个ESC,但我仍然需要从接收器读取输入PPM。为此我需要至少一个16bit定时器,我不再拥有它。
它仍然是一个8位定时器空闲位,它只能读取0..255个数字,而正常数量的escs操作是1000..2000和东西。

那么,如果我将pwm和ppm读数同时使用相同的16位定时器,会发生什么?会有用吗?它会大幅降低速度吗?我和arduino配合使用Raspberry Pi来控制数据过滤,调试和其他东西,将ppm读取移到Raspberry会更好吗?

2 个答案:

答案 0 :(得分:2)

回答你的一个问题:

  

如果我对pwm和ppm使用相同的16位定时器会发生什么   读?它会起作用吗?

是。当您的引脚更改中断触发时,您可能只是读取当前的TCNT值,以找出自上一个中断后的时间长度。这不会以任何方式干扰定时器的硬件PWM操作。

  

它会大幅降低速度吗?

没有。 PWM由专用硬件完成,同时运行的软件操作不会影响其速度,也不会为相应的定时器激活任何ISR。因此,您可以让定时器根据需要生成PWM,并仍然使用它来a)从中读取当前计数器值,并且b)将输出比较和/或溢出ISR连接到它以创建软件扩展定时器。 / p>

根据您的评论进行修改:

请注意,TCNT寄存器中的实际值是当前定时器(滴答)计数,无论PWM是否有效。此外,定时器OVERflow中断(TOV)可以在任何模式下使用。这两个属性允许通过以下步骤为任意其他时间测量任务制作软件扩展计时器:

  1. 为要使用的定时器/计数器安装并激活定时器溢出中断。在ISR中,您基本上只增加一个(volatile!)全局变量(例如timer1OvfCount),它有效地计算定时器溢出,从而扩展实际的定时器范围。然后可以将当前绝对滴答计数计算为timer1OvfCount * topTimerValue + TCNTx
  2. 当事件发生时,例如一个引脚的上升沿,在处理程序中(例如引脚更改ISR),您将读取当前定时器/连接器(TCNT)值 {{1}并将这些值存储在另一个全局变量(例如timer1OvfCount)中,从而有效地开始计时。
  3. 当第二个事件发生时,例如在一个引脚上有一个下降沿,在处理程序中(例如引脚更改ISR),您读取当前定时器/连接器(TCNT)值 startTimestamp。现在,你有timer1OvfCount中信号开始的时间戳和另一个变量中信号结束的时间戳。这两个时间戳之间的差异正是您之后的脉冲持续时间。
  4. 但要考虑两点:

    1. 使用相位修正PWM模式时,定时器将在连续向上和向下计数之间切换。这使得查找自上次TOV中断以来传递的实际滴答数更加复杂。
    2. 在首先阅读TCNT然后阅读startTimestamp和TOV ISR的一段代码之间可能存在竞争条件。这可以通过禁用中断,然后读取TCNT,然后读取timer1OvfCount,然后检查TOV中断标志来解决;如果设置了标志,则存在待处理的未处理的溢出中断 - >启用中断并重复。
    3. 但是,我非常确定有几个库函数可以维护软件扩展的计时器/计数器,为您完成所有计时器处理。

答案 1 :(得分:0)

什么是700和2000的单位?我猜usec.y你在你的问题中没有开口太多,但我发现你需要25毫秒持续时间的脉冲,其中700次使用时间可能是0度,2000可能是180度现在每个伺服的脉冲输入可以连接AVR的任何GPIO。这个GPIO为伺服提供PWM信号。所以我猜你甚至可以用一个定时器控制所有电机。用这种代码:

假设你有一个计时器,每50个usec就会产生一次中断。 现在如果你想700马达用于电机1,800用于电机2,900用于电机3& 1000 usec for motor 4然后就这样做:

#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500

unsigned short motor1=14;  // 700usec = 50x14
unsigned short motor2=16;  // 800usec
unsigned short motor3=18;  // 900usec
unsigned short motor4=20;  // 1000usec

unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;

PA.0 = 1; // IO for motor1 
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4

void timer_inturrupt_at_50usec()
{
   motor1--;motor2--;motor3--;motor4--;
   if(!motor1)
   {
      if(motor1_high_flag)
      {
          motor1_high_flag = 0;
          PA.0 = 0;
          motor1 = CYCLE_PERIOD - motor1;
      }
      if(!motor1_high_flag)
      {
          motor1_high_flag = 1;
          PA.0 = 1;
          motor1 = 14;    // this one is dummy;if you want to change duty time update this in main
      }
   }

   if(!motor2)
   {
      if(motor2_high_flag)
      {
          motor2_high_flag = 0;
          PA.1 = 0;
          motor2 = CYCLE_PERIOD - motor2;
      }
      if(!motor2_high_flag)
      {
          motor2_high_flag = 1;
          PA.1 = 1;
          motor2 = 16;
      }
   }


   if(!motor3)
   {
      if(motor3_high_flag)
      {
          motor3_high_flag = 0;
          PA.2 = 0;
          motor3 = CYCLE_PERIOD - motor3;
      }
      if(!motor3_high_flag)
      {
          motor3_high_flag = 1;
          PA.2 = 1;
          motor3 = 18;
      }
   }

   if(!motor4)
   {
      if(motor4_high_flag)
      {
          motor4_high_flag = 0;
          PA.3 = 0;
          motor4 = CYCLE_PERIOD - motor4;
      }
      if(!motor4_high_flag)
      {
          motor4_high_flag = 1;
          PA.3 = 1;
          motor4 = 19;
      }
   }
}

&安培;告诉我什么是ESC?