Servo doesn't work with InitTimersSafe() from PWM.h

Hi Everyone!

I'm making a RC boat. If I use the InitTimersSafe() function from the PWM.h - a motor works great, but my servo sets maximum angle and I can't control it. Without this function servo works, but my motor only whistles. What can I do to fix this problem?

Fragments of my program:

int InA1 = 7;
int InB1 = 8;
int PWM1 = 3;
int32_t f = 20000;

void setup() 
{   
  Serial.begin(9600);
  myservo.attach(9);

  pinMode(InA1, OUTPUT);
  pinMode(InB1, OUTPUT);
  pinMode(PWM1, OUTPUT);
  InitTimersSafe(); // this is the problem
  bool success = SetPinFrequencySafe(PWM1, f);
} 

void loop() 
{  
    Skrec();
    ustawSzybkosc();  
}

void Skrec() {
  
  if (pozycja != myservo.read()) {
    myservo.write(pozycja);
    delay(10);
  }
}
void ustawSzybkosc() {
  int moc = szybkosc*255/100;
    
  digitalWrite(InA1, LOW); 
  digitalWrite(InB1, HIGH);
   
  pwmWrite(PWM1,moc);
}

The Servo library uses Timer 1. If you want servos to work, it's best not to mess with Timer 1 and let the Servo library do what it needs to do with it. Your PWM library is messing with Timer 1, so it might not be a good mix with the Servo library. You could either go into that library and modify it to only deal with Timer 0 and Timer 2, or you can ditch that library and deal with the PWM yourself.

You could try the ServoTimer2 library

…R

ServoTimer2 library didn't help...
I tried several "PWM frequency" methods (eg http://forum.arduino.cc/index.php?topic=161643.0) but they didn't work: motor and servo don't work together.
Can you help me? I need between 16-20 kHz PWM frequency to my motor but my servo have to work.

I tried several "PWM frequency" methods (eg http://forum.arduino.cc/index.php?topic=161643.0) but they didn't work: motor and servo don't work together.

Please post the code which didn't work.

Code:

#include <Servo.h>

Servo myservo;

// sterowanie silnikiem
int InA1 = 7;
int InB1 = 8;
int PWM1 = 9;

int skret = 0;
int pozycja = 0;
int szybkosc = 0;


void setup() 
{   

  myservo.attach(3);

  pinMode(InA1, OUTPUT);
  pinMode(InB1, OUTPUT);
  pinMode(PWM1, OUTPUT);


  analogWriteSAH_Init();
} 

void loop() 
{  
    Skrec();
    ustawSzybkosc();
}


void Skrec() {
 pozycja = (skret+90);
  
  if (pozycja != myservo.read()) {
    myservo.write(pozycja);
    delay(10); 
  }
}

void ustawSzybkosc() {
  int moc = szybkosc*800/100;
    
  digitalWrite(InA1, LOW); 
  digitalWrite(InB1, HIGH);

  analogWriteSAH(moc);
}
 //*******************************************
// THIS PART IS FROM THIS TOPIC http://forum.arduino.cc/index.php?topic=161643.0 :

void analogWriteSAH_Init( void )
{
 // Stop the timer while we muck with it

 TCCR1B = (0 << ICNC1) | (0 << ICES1) | (0 << WGM13) | (0 << WGM12) | (0 << CS12) | (0 << CS11) | (0 << CS10);
 
 // Set the timer to mode 14...
 //
 // Mode  WGM13  WGM12  WGM11  WGM10  Timer/Counter Mode of Operation  TOP   Update of OCR1x at TOV1  Flag Set on
 //              CTC1   PWM11  PWM10
 // ----  -----  -----  -----  -----  -------------------------------  ----  -----------------------  -----------
 // 14    1      1      1      0      Fast PWM                         ICR1  BOTTOM                   TOP
 
 // Set output on Channel A and B to...
 //
 // COM1z1  COM1z0  Description
 // ------  ------  -----------------------------------------------------------
 // 1       0       Clear OC1A/OC1B on Compare Match (Set output to low level).
 
 TCCR1A = 
     (1 << COM1A1) | (0 << COM1A0) |   // COM1A1, COM1A0 = 1, 0
     (1 << COM1B1) | (0 << COM1B0) |
     (1 << WGM11) | (0 << WGM10);      // WGM11, WGM10 = 1, 0

 // Set TOP to...
 //
 // fclk_I/O = 16000000
 // N        = 1
 // TOP      = 799
 //
 // fOCnxPWM = fclk_I/O / (N * (1 + TOP))
 // fOCnxPWM = 16000000 / (1 * (1 + 799))
 // fOCnxPWM = 16000000 / 800
 // fOCnxPWM = 20000

 ICR1 = 799;

 // Ensure the first slope is complete

 TCNT1 = 0;

 // Ensure Channel A and B start at zero / off

 OCR1A = 0;
 OCR1B = 0;

 // We don't need no stinkin interrupts

 TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (0 << TOIE1);

 // Ensure the Channel A and B pins are configured for output
 DDRB |= (1 << DDB1);
 DDRB |= (1 << DDB2);

 // Start the timer...
 //
 // CS12  CS11  CS10  Description
 // ----  ----  ----  ------------------------
 // 0     0     1     clkI/O/1 (No prescaling)

 TCCR1B =
     (0 << ICNC1) | (0 << ICES1) |
     (1 << WGM13) | (1 << WGM12) |              // WGM13, WGM12 = 1, 1
     (0 << CS12) | (0 << CS11) | (1 << CS10);
}

void analogWriteSAH( uint16_t value )
{
  if ( (value >= 0) && (value < 800) )
  {
    OCR1A = value;
  }
}

Motor works good, using this code, but servo doesn’t work at all. I tried diffrent digital PINs for it, but it didn;t help.
If I delete "analogWriteSAH_Init() and change “analogWriteSAH(moc);” to “analogWrite(PWM1, moc);” servo works, of course, but I need about <=20 kHz PWM frequency for my motor.

Post a link to the datasheet of the motor driver that you are using?

Where does the PWM.h library come from and what is its purpose? How does it differ from analogWrite() ?

...R

I don't know exactly what type of motor is it. A shop, where I've bought the motor, doesn't know too. :confused: It's simillar to this: http://www.aliexpress.com/item/RS380-7-2V-9V-mini-dc-electric-motors-Vacuum-Cleaner-kitchen-utensils-radio-controlled-model-Drill/1254984744.html
Sorry, I can't find the datasheet for it. I'm using 7,2 V and VNH2SP30 single motor driver Monster Moto Shield.

PWM.h is from: http://forum.arduino.cc/index.php?topic=117425.0. I was using v. 05. During using analogWrite() there is a horrible sound from the motor (especially when I'm using low power) and generally the motor doesn't have as much power as using 20kHz with the PWM.h

Tenofik:
Code:

#include <Servo.h>

Servo myservo;

// sterowanie silnikiem
int InA1 = 7;
int InB1 = 8;
int PWM1 = 9;

int skret = 0;
int pozycja = 0;
int szybkosc = 0;

void setup()
{

myservo.attach(3);

pinMode(InA1, OUTPUT);
  pinMode(InB1, OUTPUT);
  pinMode(PWM1, OUTPUT);

analogWriteSAH_Init();
}

void loop()

    Skrec();
    ustawSzybkosc();
}

void Skrec() {
pozycja = (skret+90);
 
  if (pozycja != myservo.read()) {
    myservo.write(pozycja);
    delay(10);
  }
}

void ustawSzybkosc() {
  int moc = szybkosc*800/100;
   
  digitalWrite(InA1, LOW);
  digitalWrite(InB1, HIGH);

analogWriteSAH(moc);
}
//*******************************************
// THIS PART IS FROM THIS TOPIC http://forum.arduino.cc/index.php?topic=161643.0 :

void analogWriteSAH_Init( void )
{
// Stop the timer while we muck with it

TCCR1B = (0 << ICNC1) | (0 << ICES1) | (0 << WGM13) | (0 << WGM12) | (0 << CS12) | (0 << CS11) | (0 << CS10);

// Set the timer to mode 14…
//
// Mode  WGM13  WGM12  WGM11  WGM10  Timer/Counter Mode of Operation  TOP  Update of OCR1x at TOV1  Flag Set on
//              CTC1  PWM11  PWM10
// ----  -----  -----  -----  -----  -------------------------------  ----  -----------------------  -----------
// 14    1      1      1      0      Fast PWM                        ICR1  BOTTOM                  TOP

// Set output on Channel A and B to…
//
// COM1z1  COM1z0  Description
// ------  ------  -----------------------------------------------------------
// 1      0      Clear OC1A/OC1B on Compare Match (Set output to low level).

TCCR1A =
    (1 << COM1A1) | (0 << COM1A0) |  // COM1A1, COM1A0 = 1, 0
    (1 << COM1B1) | (0 << COM1B0) |
    (1 << WGM11) | (0 << WGM10);      // WGM11, WGM10 = 1, 0

// Set TOP to…
//
// fclk_I/O = 16000000
// N        = 1
// TOP      = 799
//
// fOCnxPWM = fclk_I/O / (N * (1 + TOP))
// fOCnxPWM = 16000000 / (1 * (1 + 799))
// fOCnxPWM = 16000000 / 800
// fOCnxPWM = 20000

ICR1 = 799;

// Ensure the first slope is complete

TCNT1 = 0;

// Ensure Channel A and B start at zero / off

OCR1A = 0;
OCR1B = 0;

// We don’t need no stinkin interrupts

TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (0 << TOIE1);

// Ensure the Channel A and B pins are configured for output
DDRB |= (1 << DDB1);
DDRB |= (1 << DDB2);

// Start the timer…
//
// CS12  CS11  CS10  Description
// ----  ----  ----  ------------------------
// 0    0    1    clkI/O/1 (No prescaling)

TCCR1B =
    (0 << ICNC1) | (0 << ICES1) |
    (1 << WGM13) | (1 << WGM12) |              // WGM13, WGM12 = 1, 1
    (0 << CS12) | (0 << CS11) | (1 << CS10);
}

void analogWriteSAH( uint16_t value )
{
  if ( (value >= 0) && (value < 800) )
  {
    OCR1A = value;
  }
}




Motor works good, using this code, but servo doesn't work at all. I tried diffrent digital PINs for it, but it didn;t help.
If I delete "analogWriteSAH_Init() and change "analogWriteSAH(moc);" to "analogWrite(PWM1, moc);" servo works, of course, but I need about <=20 kHz PWM frequency for my motor.

You’re still trying to use timer 1 and the servo library at the same time. If you want to use Servo, then stay away from PWM on pins 9 and 10 with timer 1. Use one of the other timers and work on different pins. You can’t do PWM on 9 or 10 and use Servo at the same time.

I can see the purpose of PWM.h - but I can't immediately see if you can specify which Timer you want to use. That would seem to be a fairly basic requirement to avoid mucking up other things.

What happened when you tried the ServoTimer2 library?

...R

It does not make me the difference which timer I use (I think). I only need PWM about <=20kHz for my motor and using servo without problems. I am and I will very grateful for your help, becouse I can’t deal with it. I’ve never used frequency changed and more then one mechanism with motor and it’s quite difficult for me.

During using ServoTimer2 library servo sets angle about 10 degrees, sometimes jump a few degrees and I can’t change it.

Tenofik:
During using ServoTimer2 library servo sets angle about 10 degrees, sometimes jump a few degrees and I can't change it.

Did you use microseconds (rather than degrees) with the ServoTimer2 library ?

This project is a good example of a common problem with libraries (and not just on the Arduino). You have now spent more time trying to figure out how to use the library in the context of your project than the time it would have taken to figure out how to modify the PWM frequency for one Timer with your own code.

...R

Here is some simple example code which sets up pin 3 on timer 2 for 20khz pwm and turns it on and off. You can not use pin 11 on Timer2 because it is taken out of commission by the mode and top value which sets up the 20 khz.

Because the set up on timer2 for 20 khz uses 100 counts, the resolution on the duty cycle setting is limited to values 1-98.

void setup() {
  setTimer2PWM_20KHZ();
  pinMode(3, OUTPUT); //Pin 3 OutputB
}

void loop()
{
  delay (5000);
  startTimer2PWM(50);
  delay (5000);
  stopTimer2PWM();
}

void setTimer2PWM_20KHZ()
{
  TCCR2A = 0;//initialize timer registers
  TCCR2B = 0;
  TCNT2 = 0;

  // Set the timer to mode 7... Fast PWM to OCR2A
  // Set output on Channe2B Pin3
  // Clear on compare match
  TCCR2A |= (1 << WGM21) | (1 << WGM20); // part of mode 7 setting
  TCCR2B |= (1 << WGM22); //third bit of mode 7
  TCCR2A |= (1 << COM2B1); //Pin 3, Clear on compare match

  //Set fast pwm to 20 khz
  TCCR2B |= (1 << CS21); //Prescaler 8 .5us per count
  OCR2A = 99; //100 counts, zero referenced = 50us = 20khz

  stopTimer2PWM();//leave turned off
}

void stopTimer2PWM()
{
  TCCR2B &= ~(1 << CS21); // set prescaler to 0, stops timer2
  TCNT2 = 0;
}

void startTimer2PWM(int dutyCycle)
{
  //OCR2B is duty cycle on Pin 3:
  //OCR2B = 49;//50% duty Cycle Square Wave
  //adjust OCR2B for motor speed
  OCR2B = dutyCycle; //value 1-98
  TCCR2B |= (1 << CS21); //Prescaler 8 .5us per tick, restarts if stopped
}