Controlling a single servo using PWM and Timer 1

I am trying to control a single servo connected to an Arduino nano using PWM. I mainly used the Atmel ATmega328P Datasheet to create the code below and my friend Google to fill my knowledge gaps on this subject. And as you might expect, it does not do what i expected it to do..

   LandjeServo::Init(int servoPin)
    {
    
        _servoPort = servoPin; // using pin D9
        _servoPinBitmask = digitalPinToBitMask(_servoPort);                             // translate pin number to port register bitmask for selected pin.
        _servoOutputRegister = portOutputRegister(digitalPinToPort(_servoPort));        // Get the output (PORT) port register for selected pin.
        _servoModeRegister = (uint8_t *)portModeRegister(digitalPinToPort(_servoPort)); // get DDR register for pin
        *_servoModeRegister |= _servoPinBitmask;                                        // Set pin to output (HIGH) in DDR registe
    
        TCCR1A = 0; // reset Timer Control Register A
        TCCR1B = 0; // reset Timer Control Register B
    
        // Set Waveform Generation Mode to mode 14, Fast PWM with TOP defined in
        // Input Capture Register ICR1
        TCCR1A = (1 << WGM11);
        TCCR1B = (1 << WGM13) | (1 << WGM12);
    
        // CPU Freq 16Mhz
        // Need interval of 20Ms ==> 0.02/(1/16000000) = 320.000 ticks
        // Using prescaler pclkIO/8 ==> resolution changes from 0,0000000625s to 0,0000005s
        // Need interval of 20Ms ==> 0.02/((1/16000000)*8) = 40000 ticks
    
        // set TOP (Period) to 20ms / 50Hz
        ICR1 = 40000;
    
        // Set min and max servo pos
        _min = 2000; // left 1ms pulse 0,0010/((1/16000000)*8)
        _max = 4000; // left 2ms pulse 0,0010/((1/16000000)*8)
    
        OCR1A = (_min + _max) / 2; // set servo initial in middle position
       
        // Clear OC1A on Compare Match (Set output to low level).
        TCCR1A ^= (1 << COM1A1);
    
         // enable timer compare interrupt to call ISR TIMER1_OVF_VECT
        TIMSK1 |= (1 << OCIE1A);
    
        // Set prescaler to start PWM
        TCCR1B = (1 << CS11);
        
    };

The servo is connected to D9, (physical pin 13, Port Pin PB1). Timer 1 (16bit) is set to Fast PWM with TOP ICR1 (mode 14). The prescaler is set to 8 so 40000 clock cycles are exactly 20ms.
Bit COM1A1 is set in TCCR1A to clear OC1A on compare match.

My expectation was that at the beginning of eacht period OC1A would be set high and changed to low when the value in OCR1A was reached. Getting me a period of 20ms and a duty cycle depending on the value set in OCR1A. When executing the code above i would expect a pulse of 1.5ms each 20ms.
Instead i do get a pulse duration of 0.295ms and a period of 8,255ms.

Why not just use the Servo library?

Note that the PWM produced by the analogWrite() function and by the hardware Timers is not what is required to control a servo.

...R

In the project/library i am building Timer0 is taken by the Arduino functions like delay and Timer2 is used to control my stepper motors. Timer2 is also used by the stepper library, so can not use it because it conflicts with the stepper motor library i wrote. I only need to control one stepper motor, and using PWM is more efficient.

And most importantly, you learn by writing the code yourself !

I am trying to understand why i am getting the (strange) period and pulse length.

When setting the OC1A on compare match (TCCR1A |= (1 << COM1A1) | (1 << COM1A0) ;) instead of clearing i do get exact the same pulse, but inversed.

macpeterr:
Timer2 is also used by the stepper library,

I thought it was a servo you want to control. And in an Uno Timer1 is used by the Servo library. And there is a special ServoTimer2 library for those that need it.

If you really do need to use a hardware Timer just get it to produce an interrupt after every 20000 - W microsecs and in the ISR put the servo pin HIGH and set the next period to W. In the next ISR call set the pin LOW and set the period to 20000 - W.

I would just use the DigitalWriteFast library to set the pins high or low.

...R

I indeed want to control a servo. And i want to control it using a PWM signal based on Timer1 because i already use Timer2 to control 2 stepper motors.

I have been debugging further and created a new program from scratch.

void setup() {
  
  uint8_t _servoPort = 9; // using pin D9
  uint8_t _servoPinBitmask = digitalPinToBitMask(_servoPort);                             // translate pin number to port register bitmask for selected pin.
  volatile uint8_t *_servoOutputRegister;
  volatile uint8_t *_servoModeRegister;
  _servoOutputRegister = portOutputRegister(digitalPinToPort(_servoPort));        // Get the output (PORT) port register for selected pin.
  _servoModeRegister = (uint8_t *)portModeRegister(digitalPinToPort(_servoPort)); // get DDR register for pin
  *_servoModeRegister |= _servoPinBitmask;                                       // Set pin to output (HIGH) in DDR registe

  // Clear OC1A on match, set on bottom
  TCCR1A |=  (1 << COM1A1);
  TCCR1A &= ~(1 << COM1A0);

  // Set OC1B on match, clear on bottom (inverted)
  TCCR1A |=  (1 << COM1B1);
  TCCR1A |=  (1 << COM1B0);

  // Fast PWM Mode 14 ICR1=TOP
  TCCR1B |=  (1 << WGM13);
  TCCR1B |=  (1 << WGM12);
  TCCR1A |=  (1 << WGM11);
  TCCR1A &= ~(1 << WGM10);

  // frequency 16000000/(8*(40000-1)) = 50Hz
  ICR1  = 0x9C40;

  // Duty cycle 25%
  OCR1A = 0x2710 ;

  // Duty cycle 50%
  OCR1B = 0x4E20 ;


  // Prescaler to CLKio/8
  TCCR1B &= ~(1 << CS12);
  TCCR1B |=  (1 << CS11);
  TCCR1B &= ~(1 << CS10);

}

void loop() {

}

This code does exactly what i expect it to do. Create a pulse with a period of 20ms (50Hz) and a duty cycle on OCR1A of 25% and 50% on OCR1B. :confused:

When moving this code to the library in LandjeServo::Init the frequency of the pulse changes to 122.4Hz and the duty cycle for OCR1A to approx 76% and for OCR1B to 47%.

So went digging further ... and could only find one difference between the INO and the library i am working on. The header file includes the Arduino.h to define uint8_t, digitalPinToBitMask, port and register names etc.

Is there somebody who can explain me why including the Arduino.h in the library screws up the timer settings while it does not in the ino file. The ino file obviously includes the Arduino.h by default since i am able to use the types uint8_t etc.

What does work

void setup() {
  
  uint8_t _servoPort = 9; // using pin D9
  uint8_t _servoPinBitmask = digitalPinToBitMask(_servoPort);                             // translate pin number to port register bitmask for selected pin.
  volatile uint8_t *_servoOutputRegister;
  volatile uint8_t *_servoModeRegister;
  _servoOutputRegister = portOutputRegister(digitalPinToPort(_servoPort));        // Get the output (PORT) port register for selected pin.
  _servoModeRegister = (uint8_t *)portModeRegister(digitalPinToPort(_servoPort)); // get DDR register for pin
  *_servoModeRegister |= _servoPinBitmask;                                       // Set pin to output (HIGH) in DDR registe

  // Clear OC1A on match, set on bottom
  TCCR1A |=  (1 << COM1A1);
  TCCR1A &= ~(1 << COM1A0);

  // Set OC1B on match, clear on bottom (inverted)
  TCCR1A |=  (1 << COM1B1);
  TCCR1A |=  (1 << COM1B0);

  // Fast PWM Mode 14 ICR1=TOP
  TCCR1B |=  (1 << WGM13);
  TCCR1B |=  (1 << WGM12);
  TCCR1A |=  (1 << WGM11);
  TCCR1A &= ~(1 << WGM10);

  // frequency 16000000/(8*(40000-1)) = 50Hz
  ICR1  = 0x9C40;

  // Duty cycle 25%
  OCR1A = 0x2710 ;

  // Duty cycle 50%
  OCR1B = 0x4E20 ;


  // Prescaler to CLKio/8
  TCCR1B &= ~(1 << CS12);
  TCCR1B |=  (1 << CS11);
  TCCR1B &= ~(1 << CS10);

}

void loop() {

}

What does not work

#include <LandjeRobot.h>

int servoPin = 9 ;
LandjeRobot landjerobot(servoPin) ;

void setup() {
}

void loop() {
}

LandjeRobot.h

#ifndef LandjeRobot_h
#define LandjeRobot_h

class LandjeRobot
{
public:
   LandjeRobot(int);

private:
  headServoPin ;
};

#endif

LandjeRobot.cpp

#include "LandjeRobot.h"

#include "LandjeServo.h"

LandjeServo z;

LandjeRobot::LandjeRobot(int headServo)
{
    _headServoPin = headServo ;

    z.Init(_headServoPin) ;

}

LandjeServo.h

#ifndef LandjeServo_h
#define LandjeServo_h

#include <Arduino.h>

class LandjeServo
{

public:
  Init(int servo);

private:
  uint8_t _servoPort;
  uint8_t _servoPinBitmask;
  volatile uint8_t *_servoOutputRegister;
  volatile uint8_t *_servoModeRegister;
  unsigned int _min;
  unsigned int _max;
};

#endif

LandjeServo.cpp

#include <Arduino.h>
#include "LandjeServo.h"

LandjeServo::Init(int servoPin)
{

    _servoPort = servoPin;                                                          // using pin D9
    _servoPinBitmask = digitalPinToBitMask(_servoPort);                             // translate pin number to port register bitmask for selected pin.
    _servoOutputRegister = portOutputRegister(digitalPinToPort(_servoPort));        // Get the output (PORT) port register for selected pin.
    _servoModeRegister = (uint8_t *)portModeRegister(digitalPinToPort(_servoPort)); // get DDR register for pin
    *_servoModeRegister |= _servoPinBitmask;                                        // Set pin to output (HIGH) in DDR registe

   // Clear OC1A on match, set on bottom
    TCCR1A |= (1 << COM1A1);
    TCCR1A &= ~(1 << COM1A0);

    // Set OC1B on match, clear on bottom (inverted)
    TCCR1A |= (1 << COM1B1);
    TCCR1A |= (1 << COM1B0);

    // Fast PWM Mode 14 ICR1=TOP
    TCCR1B |= (1 << WGM13);
    TCCR1B |= (1 << WGM12);
    TCCR1A |= (1 << WGM11);
    TCCR1A &= ~(1 << WGM10);

    // frequency 16000000/(8*(40000-1)) = 50Hz
    ICR1 = 0x9C40;

    // Duty cycle 25%
    OCR1A = 0x2710;

    // Duty cycle 50%
    OCR1B = 0x4E20;

    // Prescaler to CLKio/8
    TCCR1B &= ~(1 << CS12);
    TCCR1B |= (1 << CS11);
    TCCR1B &= ~(1 << CS10);
};

macpeterr:
I indeed want to control a servo. And i want to control it using a PWM signal based on Timer1 because i already use Timer2 to control 2 stepper motors.

Have you actually tried using the Servo library?

...R

Yes, but what is wrong with answering the question?

macpeterr:
Yes, but what is wrong with answering the question?

It's just a waste of time if the Servo library will do the job.

...R