Timer prescaling question

Hi to all,

I am interested mainly in radio controlled aircraft and working on projects which will be used in my R/C models. This necessitates precise servo control and to be able to learn microcontrollers in depth, I try to avoid using ready libraries for now. That means, I want to write my own code as much as possible.
Most of my projects for R/C aircraft must read a PWM signal and/or produce a PWM signal. Standart IDE commands like delayMicroseconds() or micros() has a resolution of 4 microseconds and this is not accurate enough for a RC servo. I need at least 1 microsecond resolution. This requires the usage of timers and timer interrupts directly.
As a first step I have written a test code which utilises Timer1 on Arduino Uno. As this timer is a 16 bit timer, everything was fine. I could produce output pulses varying between 800 - 2200 microseconds very accurately.
My second plan was to adapt the code to a 8 bit timer. As weight and dimensions are very important in RC aircraft, most of my projects utilise an Attiny85. Attiny 85 does not have a 16 bit timer. So I planned to develop and debug the code on an 8 bit timer of the Arduino Uno. After everything has been fixed I could move the code to Attiny.
As the timer would be an 8 bit type, I had to use a counter variable in the ISR and then go on accordingly.
Now the problem...
After several tries I couldn't get the program running. Then, I wrote a very simple code to test the timers.

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

  cli(); // Disable interrupts
  TCCR2A = 0; // Clear Timer2 control register A
  TCCR2B = 0; // Clear Timer2 control register B

  TCCR2B = 7; // This means CS22, CS21, CS20 bits are set and this should lead to a 1024 prescaled clock

  TCNT2 = 0; // Clear Timer2
  sei(); // Enable interrupts

}

void loop() 
{
  Serial.println(TCNT2);

}

I have tried various values for the prescaler select bits.
For TCCR2B = 1 - 5 there is no problem. TCNT2 keeps updating regularly. But if I use 6 or 7 (6 means 256 prescaler and 7 means 1024 prescaler) Timer2 doesn't run!

I have read through the Atmel Atmega328 datasheet and couldn't find any clue.

Now my question:

Why does Timer0 and Timer2 not run if a prescaler of 256 or 1024 is selected?

Thank you very much for your attention...

When I run this code on an Uno, I get a list of numbers that starts small, increments by five or six until it gets near 255, and then starts small again. When I run it with TCCR2B = 6, I get the same thing, with a larger increment. Generally, I get what I’d expect from Timer2. So, I don’t really have much to contribute about Timer2.

Timer0, though, doesn’t use the system clock in modes 6 and 7. Instead, it counts external clock pulses on the T0 pin, PD4, pin 6 on the IC, digital pin 4 on the Arduino. In mode 6, it clocks on the falling edge, and in mode 7, it clocks on the rising edge. Unless you have state changes on the T0 input, Timer 0 won’t advance in modes 6 and 7.

Why does Timer0 and Timer2 not run if a prescaler of 256 or 1024 is selected?

They do.

Study the datasheet more carefully. The definitions of the bits in TCCRxB (in particular CSxx) are different between the different timers, and the use of and number of divisors in the prescalers is different.

First of all, thank you very much for your advices. have read the datasheet more carefully and I am now able to start the timers, to count overflows etc.
Back to my original problem…
I want to produce 1000 - 2000 microsecond pulses with a 1 microsecond resolution. I want to do this using a 8 bit timer. So I decided to use Timer2.

These are the variable declarations

const int out = 5; // Pulse output on digital pin 5
volatile byte counterH;
unsigned int pwmout;
volatile byte pwmH;
volatile byte pwmL;

This is the setup part

void setup()
{
  pinMode(out, OUTPUT);
  cli(); // Disable global interrupts
  TCCR2B = 2; // Timer2 prescaler 8
  TIMSK2 = 1; // Timer2 Overflow Interrupt enable
  TCNT2 = 0; // Reset Timer2
  sei();
}

This is the ISR

ISR(TIMER2_OVF_vect)
{
  counterH++;
}

Main loop for testing. It is supposed to drive the servo from one extreme to the other and then back continuously and a single step shall be 1 microsecond).

void loop()
{
  for (int n = 1000; n < 2000; n = n++)
  {
    pwmout = n;
    servoout();
  }
  for (int n = 2000; n > 1000; n = n--)
  {
    pwmout = n;
    servoout();
  }
}

Finally this is the servo output subroutine

void servoout()
{
  cli(); // Disable global interrupts
  TIMSK2 = 1; // Timer2 Overflow Interrupt enable
  pwmH = pwmout / 256;
  pwmL = pwmout - (pwmH * 256);
  TCNT2 = 0; // Reset Timer2
  counterH = 0;

  digitalWrite(out, HIGH); // Start of pulse
  sei();
  while (counterH < pwmH)
  {

  }
  cli();
    while (TCNT2 < pwmL)
    {
    }
    digitalWrite(out, LOW); // End of pulse


    counterH = 0; // Wait for approximately 20 milliseconds
    TCNT2 = 0;
    sei();
    while (counterH < 150)
    {
    }
}

This code works but there is a serious problem. The servo starts it’s movement with very fine steps and after maybe 10 - 15 degrees of rotation it rotates suddenly 10 - 15 degrees and then continues the rotation in 1 microsecond steps. After 10 - 15 degrees the same thing happens again. It is difficult to explain this behaviour but the servo movement is like this: 1000, 1001, 1002, 1003, 1004,…1100, then abruptly 1200, 1201, 1202, 1203, 1204,… 1300, then abruptly, 1400, 1401, 1402, 1403,…

Now I am trying to find a method for troubleshooting…

sumeryamaner:
Now I am trying to find a method for troubleshooting...

The first method that I would recommend is to Serial.print() some of the things that you calculate, to see if they're what you expect. Try printing the value of pwmout right before you call servoout(), and see if it's what you think it is.

I'll also recommend that you rethink how you're measuring the time interval. It looks like you're setting Timer2 to 0.5 microseconds per tick, and then treating it as if it were set to 1 microsecond per tick. At 16 MHz, a clock prescaler of 8 will yield to a timer tick of 0.5 microseconds.

I think that there are yet other things that will need another look. But, right now, I think that pwmout isn't incrementing, and I think that the pulse length is substantially shorter than 1000 microseconds. Unitl those are fixed, I don't think that more subtle issues will matter much.

This is what I get…

I do not think that your code handles the situations where pwmout is a multiple of 256 , and pwmL =0.

Try adding a conditional test on pwmL==0.

void servoout()
{
  cli(); // Disable global interrupts
  TIMSK2 = 1; // Timer2 Overflow Interrupt enable
  pwmH = pwmout / 256;
  pwmL = pwmout - (pwmH * 256);
  TCNT2 = 0; // Reset Timer2
  counterH = 0;

  digitalWrite(out, HIGH); // Start of pulse
  sei();
  while (counterH < pwmH)
  {

  }
  cli();

  //try adding this conditonal test on pwmL

   if (pwmL==0)
    {
       digitalWrite(out, LOW); // End of pulse
     }
else 
{
    while (TCNT2 < pwmL)
    {
    }
    digitalWrite(out, LOW); // End of pulse
}

    counterH = 0; // Wait for approximately 20 milliseconds
    TCNT2 = 0;
    sei();
    while (counterH < 150)
    {
    }
}

Cattledog, thank you for your help and advice. Testing that pwmL is greater than 0 didn’t change anything. After trial and error there is a working code now.

void servoout()
{
  pwmL = pwmout & 255;
  pwmH = pwmout >> 8;
  digitalWrite(out, HIGH);
  TCNT2 = 0;
  counter = 0;
  while (counter <= pwmH)
  {
  }
  TCNT2 = 0;
  counter = 0;
    while (counter <= pwmL)
    {
      counter = TCNT2;
    }
  digitalWrite(out, LOW);
  TCNT2 = 0;
  counter = 0;
  while (counter <= 78)
  {
  }
}

I have tested it using a real servo and a potentiometer. The main loop is as follows:

void loop()
{
  pwmin = analogRead(inpt);
  pwmout = 2000 + pwmin * 2;
  servoout();
}

I can very precisely position the servo now but I have no idea what was wrong with the previous versions. :slight_smile:

I'm not sure about how you are using the servo's, but I wonder about your overall design. Since the pulse width and the repeat period,(which now looks like 10ms) are both generated with blocking while loops, how can other things happen in your program?

As I said earlier, my primary aim was to achieve 1 uS resolution for the servo pulses using an 8 bit timer. This piece of code will be used in very small modules like servo reverser, servo slower, v-tail mixer etc. The blocking nature of the code is unimportant for these applications. For more complicated modules like servo synchroniser I will use an Arduino Nano which has a 16 bit timer and it is not difficult to produce non-blocking code for PWM pulses.
And...
You are right, I have to double the 78 to achieve a 20 mS frame.
Thank you very much.