0

I'm dealing with arduino mega based quadcopter and trying to make PWM frequency for 4 motors - 400hz each.
I've found an interesting solution where 4 ATmega2560 16bit timers are used to control 4 ESCs with PWM so it could reach 400hz frequency. 700 to 2000µs are normal pulse widths ESC are dealing with.
1sec/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)

The problem is to make it work each PWM should be controlled with 1 16bit timer. Otherwize, say, 2 escs on 1 timer would give 200hz. So all of 16bit timers are busy controlling 4 ESC but I still need to read input PPM from receiver. To do so I need at least one more 16bit timer which I don't have anymore.
It's still one 8bit timer free bit it can only read 0..255 numbers while normal number escs operate with are 1000..2000 and stuff.

So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work? Would it decrease speed drastically?
I have arduino working in pair with Raspberry Pi which controls data filtering, debugging, and stuff, is it better to move ppm reading to Raspberry?

JimmyB
  • 12,101
  • 2
  • 28
  • 44
user3081123
  • 425
  • 6
  • 15
  • How do you do that "ppm reading"? Are you using input capture mode of a timer? Or pin change interrupt? - An 8 bit counter can be extended via software to arbitrary width; if done carefully (ISR latency!), this also works with input capturing. – JimmyB Jan 12 '15 at 12:22
  • I'm using pin change interrupt. Thanks, software 8bit counter extending might be a solution, thanks for new space to google. – user3081123 Jan 12 '15 at 14:01
  • What resolution of the PWM signal do you need? I.e., how many steps do you want between stop and full speed? This number multiplied with those 400Hz gives an indication of how fast your PWM will have to be, and allows to judge if software PWM is an option here. – JimmyB Jan 12 '15 at 15:27
  • By the way, the 16-bit timers each have *three* output compare units. So you should easily be able to drive *three* PWM signals from output compare match ISRs of a single 16-bit timer. (If you don't need extreme precision; assume you may get jitter of 100 or so CPU clock cycles in the worst case.) – JimmyB Jan 12 '15 at 15:34

2 Answers2

2

To answer one of your questions:

So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work?

Yes. When your pin change interrupt fires you may just read the current TCNT value to find out how long it has been since the last one. This will not in any way interfere with the timer's hardware PWM operation.

Would it decrease speed drastically?

No. PWM is done by dedicated hardware, software operations running at the same time will not affect its speed and neither will any ISRs you may have activated for the corresponding timer. Hence, you can let the timer generate the PWM as desired and still use it to a) read the current counter value from it and b) have an output compare and/or overflow ISR hooked to it to create a software-extended timer.

Edit in response to your comment:

Note that the actual value in the TCNT register is the current timer (tick) count at any moment, irrespective of whether PWM is active or not. Also, the Timer OVerflow interrupt (TOV) can be used in any mode. These two properties allow to make a software-extended timer for arbitrary other time measurement tasks via the following steps:

  1. Install and activate a timer overflow interrupt for the timer/counter you want to use. In the ISR you basically just increment a (volatile!) global variable (timer1OvfCount for example), which effectively counts timer overflows and thus extends the actual timer range. The current absolute tick count can then be calculated as timer1OvfCount * topTimerValue + TCNTx.
  2. When an event occurs, e.g a rising edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount and store these values in another global variable (e.g. startTimestamp), effectively starting your time measurement.
  3. When the second event occurs, e.g. a falling edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount. Now you have the timestamp of the start of the signal in startTimestamp and the timestamp of the end of the signal in another variable. The difference between these two timestamps is exactly the duration of the pulse you're after.

Two points to consider though:

  1. When using phase-correct PWM modes the timer will alternate between counting up and down successively. This makes finding the actual number of ticks passed since the last TOV interrupt a little more complicated.
  2. There may be a race condition between one piece of code first reading TCNT and then reading timer1OvfCount, and the TOV ISR. This can be countered by disabling interrupts, then reading TCNT, then reading timer1OvfCount, and then checking the TOV interrupt flag; if the flag is set, there's a pending, un-handled overflow interrupt -> enable interrupts and repeat.

However, I'm pretty sure there are a couple of library functions around to maintain software-extended timer/counters that do all the timer-handling for you.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • Thank you, your answer is extremely useful for me. I also didn't know 16bit timer has 3 comparators. Now it's time for me to research if servo lib uses all 3 of them at the same time if one timer controls 3 pwms. If not, I'll write my own program. Still it may be no need to make one if ppm reading wouldn't affect pwm frequency. *BB – user3081123 Jan 13 '15 at 19:59
  • Hey, Hanno. I've returned from vacation and started digging your answer and few thing's are not clear enough for me. Imagine standard servo lib is being used, means only one comparator(OCRnA) is being used per timer. So we need to set widths of 700, 800, 700 and 1000 for each servo and read ppm. TCNT1 starts count to OCR1A = 700, TCNT3 to OCR3A = 800, TCNT4 to OCR4A = 700, TCNT5 to OCR5A = 1000. So all TCNT registers are busy counting. Then we've got an ppm high signal indicating start of reading value, so TCNT should start ticking but all of them are busy counting PWM widths. Don't get it. – user3081123 Jan 20 '15 at 00:15
  • Thank you once again. You helped me really much. – user3081123 Jan 23 '15 at 22:01
0

what is unit of 700 and 2000?I guess usec.You have not exaplained much in your question but i identified that you need pulses of 25msec duration in which 700 usec on time may be 0 degree and 2000 may be for 180 degree now pulse input of each servo may be attached with any GPIO of AVR.and this GPIOs provide PWM signal to Servo.so i guess you can even control this all motors with only one timer.With this kind of code:

suppose you have a timer that genrate inturrupt at every 50 usec. now if you want 700 usec for motor1,800 usec for motor 2,900 usec for motor 3 & 1000 usec for motor 4 then just do this:

#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;
      }
   }
}

& tell me what is ESC?

prt
  • 205
  • 3
  • 11
  • 1
    I think I've explained my question poorly. What I use is not servo but a brushless motor. Using servo library on ESCs(Electronic Speed Controller) provides changing of RPM Revolutions per minute, where for my ESC minimum pulse length is 700us and stands for motor not moving to pulse length 2000us where motor is at full speed. So the point is to make 4 speed controllers receive 400hz PWM signal which is only possible if using 1 timer per ESC. If 2 ESCs per timer is used, it gives 2x processing slow which is 200hz. Using GPIO instead of PWM looks to be decreasing frequency as well. – user3081123 Jan 12 '15 at 15:07
  • OK.But why don't you try above program it will easily generate 400 hz signals on four different channel using only one 16 bit timer. – prt Jan 13 '15 at 04:59