0

我正在处理基于 arduino mega 的四轴飞行器,并试图为 4 个电机制作 PWM 频率 - 每个电机 400hz。
我发现了一个有趣的解决方案,其中使用 4 个 ATmega2560 16 位定时器来控制 4 个带 PWM 的 ESC,因此它可以达到 400hz 的频率。700 到 2000µs 是 ESC 处理的正常脉冲宽度。
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 个 16 位定时器控制。否则,比如说,1 个计时器上的 2 个 escs 将提供 200hz。所以所有的 16 位定时器都忙于控制 4 个 ESC,但我仍然需要从接收器读取输入 PPM。为此,我至少需要一个我不再拥有的 16 位计时器。
它仍然是一个 8 位定时器空闲位,它只能读取 0..255 个数字,而正常的数字 escs 操作是 1000..2000 等等。

那么如果我对 pwm 和 ppm 读数都使用相同的 16 位计时器会发生什么?它会起作用吗?会不会大幅度降低速度?
我有 arduino 与 Raspberry Pi 一起工作,它控制数据过滤、调试和其他东西,将 ppm 读数移动到 Raspberry 是否更好?

4

2 回答 2

2

要回答您的问题之一:

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

是的。当您的引脚更改中断触发时,您可能只需读取当前的 TCNT 值即可了解自上一次中断以来已经过去了多长时间。这不会以任何方式干扰定时器的硬件 PWM 操作。

会不会大幅度降低速度?

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

编辑以回应您的评论:

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

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

不过要考虑两点:

  1. 当使用相位校正 PWM 模式时,定时器将在连续向上和向下计数之间交替。这使得查找自上次 TOV 中断以来经过的实际滴答数变得更加复杂。
  2. 一段代码先读取 TCNT 然后读取timer1OvfCount与 TOV ISR 之间可能存在竞争条件。这可以通过禁用中断,然后读取 TCNT,然后读取timer1OvfCount,然后检查 TOV 中断标志​​来解决;如果设置了标志,则有一个未处理的、未处理的溢出中断-> 启用中断并重复。

但是,我很确定有几个库函数可以维护软件扩展的定时器/计数器,它们可以为您完成所有的定时器处理。

于 2015-01-12T15:42:59.237 回答
0

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

假设你有一个每 50 微秒产生一次中断的计时器。现在,如果您想要电机 700 usec 电机 1,800 usec 电机 2,900 usec 电机 3 和 1000 usec 电机 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?

于 2015-01-12T13:58:45.660 回答