0

I have extracted the following code from the multiwii project. It generates a ~490-500hz pwm signal on pin 6 which is used to drive an esc.

I want to change this frequency down to 50Hz to drive a servo instead. But ive had next to no experience at this level of avr programming, and dont want to loose anymore days to it, if its a dead end. (ie not possible) (i have tried reading various websites on the topic)

void setup() { 
    pinMode(6, OUTPUT);
    TCCR4E |= (1<<ENHC4); // enhanced pwm mode
    TCCR4B &= ~(1<<CS41); TCCR4B |= (1<<CS42)|(1<<CS40); // prescaler to 16
    TCCR4D |= (1<<WGM40); TC4H = 0x3; OCR4C = 0xFF; // phase and frequency correct mode & top to 1023 but with enhanced pwm mode we have 2047

    TCCR4C |= (1<<COM4D1)|(1<<PWM4D); // connect pin 6 to timer 4 channel D   


}

void loop() {
    //write out
    int value = 1500;
    TC4H = value>>8; 
    OCR4D = (value&0xFF);
}

here is the new code so far working off the solution provided below:

void setup() {   
    pinMode(6, OUTPUT);
    TCCR4B &= ~(_BV(CS43) | _BV(CS42) | _BV(CS41) | _BV(CS40));
    TCCR4B |= _BV(CS43) | _BV(CS41);
    TCCR4D &= ~(_BV(WGM41) | _BV(WGM40));
    TC4H = 624 >> 8; // B10 0x2
    OCR4C = 624 & 0xff;//B1110000 0x70
    TCCR4C |= (1<<COM4D1)|(1<<PWM4D);
}
void loop() {
    //write out
    //30 - 60 gives 1000 to 2000 us, but that only allows 30 increments between ??
    int value = 30;
    TC4H = value >> 8;
    OCR4D = value & 0xff;
}

the wave form i want:

the wave form i want

2

Yeah... sometimes it's easier to scrap what's already there and start fresh.

We need to:

  • Pick a suitable prescaler to get close to the desired cycle length
  • Fine-tune the value in TOP in order to get an exact value

Assuming this is a 16MHz Leonardo, and we have Fast 10-bit PWM (PWM4x = 1, WGM4x = 0b00) available:

16MHz / 50Hz / (1 << 10) = 312.5

Therefore we need a prescaler of 512 (CS4x = 0b1010).

16MHz / 50Hz / 512 = 625

Therefore we need a TOP of 624 (OCR4C = 624).

TCCR4B &= ~(_BV(CS43) | _BV(CS42) | _BV(CS41) | _BV(CS40));
TCCR4B |= _BV(CS43) | _BV(CS41);
TCCR4D &= ~(_BV(WGM41) | _BV(WGM40));
TC4H = 624 >> 8;
OCR4C = 624 & 0xff;

And then you set COM4xn and PWM4x to appropriate values for whichever pin(s) you want to output PWM on, and set OCR4x to a value between 0 and 624 (using TC4H appropriately; see the datasheet for details) to control the duty cycle.

  • ok, its working but i think we 'overlooked' something important i forgot to mention... i need the duty cycle to suit a servo, so between 1 and 2 milliseconds. the duty im getting from your example is much bigger 2ms to 19.2 ms its like 10x too much, maybe we dropped a decimal ? (but the frequency is correct) – Hayden Thring Jul 19 '15 at 11:21
  • ive added the code i have put together from your help above into the question. if i use a value between 30 and 60 it gives me the correct duty, but this only allows 30 steps .. ? – Hayden Thring Jul 19 '15 at 12:41
  • @HaydenThring: 50Hz means a 20ms period. If that's too long then use a higher frequency. The Arduino libraries use 490Hz. – Ignacio Vazquez-Abrams Jul 19 '15 at 14:52
  • i have added a screenshot of the waveform i want, it needs to be 50hz, which is what im getting from your code, but there is a lack of resolution in the settable values to get that duty. – Hayden Thring Jul 21 '15 at 23:35
  • 1
    @HaydenThring: If you need that resolution at that low a frequency then you'll need to use timer 1 or 3 instead, in wavegen mode 14. – Ignacio Vazquez-Abrams Jul 22 '15 at 0:06
2

To get finer resolution use Timer 1 - as Ignacio Vazquez-Abrams suggested.

void setup ()
  {
  pinMode (10, OUTPUT);   // OC1B on the Leonardo / Micro
  TCCR1A = 0;   // stop Timer 1
  TCCR1B = 0;   //     ditto

  // set up Timer 1
  TCCR1A = bit (COM1B1) | bit (WGM11) | bit (WGM10);  // toggle OC1B on Compare Match
  TCCR1B = bit (WGM13) | bit (CS11);   // PWM phase correct, prescaler of 8
  OCR1A = 20000;  // frequency: 50 Hz
  OCR1B = 1000;   // duty cycle - 1 ms
  }  // end of setup

void loop ()
  {
  // whatever 
  }  // end of loop

As you can see from the scope output, the pulse width is 1 ms, which extremely conveniently corresponds to the duty cycle of 1000. Thus, therefore, the duty cycle figure is exactly related to microseconds.

Also you can see that the frequency is 50 Hz.

50 Hz Timer 1 example

This uses a prescaler of 8 to give high resolution. It takes two pulses to make one "cycle" (one on, one off) and thus it effectively divides the system clock by 16. Since the system clock is 16 MHz, that is why one "unit" is 1 µs.

The OCR1A figure is arrived at easily:

 1000000 / 50 = 20000  (1 MHz / 50 Hz)

Now you can just tweak your desired pulse width knowing that each unit is 1 µs. So for 2 ms OCR1B would need to be 2000.

  • I cant use this on pin D6 though can i ? – Hayden Thring Jul 22 '15 at 6:46
  • On the arduino micro can i use 3 pins using this code? if yes which ones? – cocco Aug 19 '15 at 23:04
  • @HaydenThring - nope, you cant use D6 for this if you are not using Timer 1 as suggested – techniche Sep 18 '17 at 11:11

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.