1

I'm using the Registers of the Arduino micro to get a signal for Electronic Speed Controllers (ESC) for brushless motors. As far as I read, ESCs interpret everything they can, while just ignoring too fast update rates. If somebody knows better, this would be a great time to yell.

The goal is to get a digital signal, where a timespan between 1 and 2 ms is HIGH and the rest low. Ideally I want this at 50-400Hz, since 488Hz is the fastest possible frequency, and 50 Hz the standard Servo frequency.

I try to use Phase Correct Mode to count up and down continuously. As the value in the counter reaches my set OCRnx on the way up, it sets the output pin LOW, as it reaches the same value on the way down, after reaching TOP(value), it sets the output HIGH. In ATmega documentation terms, this is NON-inverted Phase-Correct-PWM.

So I set the Registers DDR (to set the pin as output), TCCR1A and TCCR1B and ICR1 (TOPvalue = between 20000 and 2500).

Now using OCR1A = 1000 should give me a pulse width of 1000ms. This is the expected or target behaviour.

My testing code is:

// using a 16-bit timer to generate PWM signals for ESCs

// Phase Correct Mode counts from Bottom to Top and back to Bottom
// with a prescaler of 8
// motor signal (OCRnx) uses values of 1000 (idle) - 2000 (max)
// which directly corresponds to microseconds HIGH-time
// setting the range to (BOTTOM) 0 - TOPvalue sets the frequency

// two important formulas are:
// frequency(PWM) = F_CPU / (2*Prescalar*TOPvalue)
//
// PulseWidth [s] = High-tick-count / (F_CPU/Prescalar)
// High-tick-count = 2 * OCR-value

// n = number of timer (1,3)
// x = A,B,C


#include "Arduino.h"
// #include <Arduino.h>
// #define DEBUG
// #include <Moto.h>

#define PRESCALAR 8
#define PWM_FREQUENCY 50
#define TOP_VALUE F_CPU/(2*PRESCALAR*PWM_FREQUENCY)


uint16_t dutytime = 1000; // this gives idle throttle signal


void setup(void) {
  Serial.begin(115200);
  // moto.setup();

  // TIMER 1
  // TIMSK1 = 0; // disable Interrupts for timer1
  DDRB |= (1<<DDB5); // D9 Output
  DDRC |= (1<<DDC6); // D5 Output

  // set 1000ms to start motors in idle mode
  OCR1A = 1000L;
  OCR3A = 1000L;

  // bits 7,5,3 PWM Output on pins A,B,C respectively
  // bits 1,0 WGM_1,0: phase correction mode
  // TCCR1A |= B10000010; // in hex: 0xAA for all 3 = B 1010 1010
  TCCR1A |= (1<<COM1A1)|(1<<WGM11);
  // bit 4,3 WGM_3,2: phase correction mode
  // bits 2,1,0 Prescalar: 8
  // TCCR1B |= B00010010; // in hex: 0x12; B 0001 0010
  TCCR1B |= (1<<WGM13)|(1<<CS11);
  ICR1 = TOP_VALUE;

  // TIMER 3
  // TIMSK3 = 0; // disable Interrupts for timer3
  // bit 7 PWM Output on pin A
  // bits 1,0 WGM_1,0: phase correction mode
  TCCR3A |= B10000010; // in hex: 0x82; // B 1000 0010
  // bit 4,3 WGM_3,2: phase correction mode
  // bits 2,1,0 Prescalar: 8
  TCCR3B |= B00010010; // in hex: 0x12; B 0001 0010
  ICR3 = TOP_VALUE; // TOPvalue = 2048

}//setup


void loop(void) {

  // Serial.println(TOP_VALUE);
  // delay(500);
  while (dutytime < 2000) {
    dutytime += 150;
    OCR1A = dutytime;
    OCR3A = dutytime;
    Serial.println(dutytime);
    delay(1500);
  }
  while (dutytime > 1000) {
    dutytime -= 150;
    OCR1A = dutytime;
    OCR3A = dutytime;
    Serial.println(dutytime);
    delay(1500);
  }

}//loop

I repeated the settings for timer3 to get something to compare to. Both do give the same: if i delete the loop and just write loop{} nothing happens. The pins are high and seem to stay so. With the while loops activated, my multimeter gives me 1Hz (which are the changes of course) and between 99.9% duty cycle and 96%. An led, which i connected to it blinks every second a very short time.

What am I doing wrong?

  • I don't understand what you want to achieve. Pulse width of 1000ms at a sample rate of 488.28Hz that seems contradictory, as the maximum pulse width can only be 2ms at 488Hz. – Gerben Oct 7 '14 at 12:26
  • 2048 to be exact for this case. Why is that contradictory, if I want to have pulses between 1 and 2 ms (something around 48% and 97% duty cycle)? – mike Oct 7 '14 at 12:39
  • That doesn't really make it any clearer. Seems I can't help you then. – Gerben Oct 7 '14 at 14:36
  • I'm not 100% familiar with Arduino timers, but here is how I did PWM on an MSP430 with timers: imgur.com/2aOnW2g Essentially, you count up, and reset. When the timer hits the set value, it does the rising edge pulse, and when it hits the max count, it resets both the pulse and the timer. The alternative of this is to do the opposite: set the pulse high until the timer hits the threshold value, etc. – Ian M Oct 10 '14 at 12:19
  • Thanks. What you suggest is in Atmels Documentation called fast PWM, where the counters just count until overflow. I however use phase-corrected PWM, where it counts up and down. This gives a more symmetrical output (and according to them) is prefered over fast PWM. – mike Oct 10 '14 at 14:04
0

What I did wrong was using |= instead of = . Apparently Arduino set some of these Bits on start, which didn't change. Now it works like a charm and I added a frequency choice, which works for at least 50,200,400,488 Hz. Everything in between should work aswell!

Working testcode for timer1 with serial input of HIGH-time:

#define PRESCALAR 8
#define PWM_FREQUENCY 50
#define TOP_VALUE F_CPU/(2*PRESCALAR*PWM_FREQUENCY)

uint16_t dutytime = 1000; // this gives idle throttle signal

void setup(void) {
  Serial.begin(115200);

  // TIMER 1
  DDRB |= (1<<DDB5); // D9 Output

  // set 1000ms to start motors in idle mode
  OCR1A = 1000L;

  // bits 7,5,3 PWM Output on pins A,B,C respectively (COMnx1)
  // bits 1,0 WGM1(1,0): phase correction mode
  TCCR1A = B10000010; // in hex: 0xAA for all 3 = B 1010 1010
  // bit 4,3 WGM_3,2: phase correction mode
  // bits 2,1,0 Prescalar: 8
  TCCR1B = B00010010; // in hex: 0x12; B 0001 0010
  ICR1 = TOP_VALUE;
}//setup


void loop(void) {
  // Serial.println(TOP_VALUE);
  dutytime = serial_input();
  if (dutytime) {
    OCR1A = dutytime;
  }
  delay(500);
}//loop


float serial_input() {
  double num = 0;
  while(Serial.available()) {
    delay(2);
    char ser = Serial.read();
    if(isDigit(ser)) {
      num = (num*10)+(ser-48);
    }else{ break; }
  }
  if (num) {Serial.println(num);}
  return num;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.