2

I am working on a project which requires use of 16bit timer . I am using Arduino Uno (Atmega 328p) board and Timer_1 (16 bit timer ) in Overflow mode. i want to use 1024 prescaler settings.

According to the datasheet of the controller i have to set [b]CS10 and CS12[/b] bits to use 1024 prescaler but the problem is OverFlow interrupt routine is never fired when 1024 or 256 prescaler is selected .

The Following code works just fine with Prescaler set to 8 or 64.

#include <inttypes.h> 

volatile uint8_t sCount = 0;

void setup()
{
      Timer_one_16_init(); // configure the timer 

      pinMode(13,OUTPUT);

}

void loop()
{
    // do nothing 
}

void Timer_one_16_init(){

    TCCR1A = 0;

    // set the pre-scaler to 1024 (slowest)

    TCCR1B |= (1<<CS12)|(1<<CS10);

    // set the bottom / starting value

    TCNT1 = 0x00;


    // set Overflow interrupt

    TIMSK1 |= (1<<TOIE1);


    // enable global interrupt 
    sei();


}


ISR(TIMER1_OVF_vect){

    sCount++;

    if (sCount == 60)
    {
        digitalWrite(13,!digitalRead(13));
        sCount = 0;

    }

}

i have no idea why this is happening. i have done everything which is given in the datasheet .

Am i doing something wrong ?

Thanks in advance

2 Answers 2

2

You must write the clock-select bits to the correct value. The simplest way to do it is to write the proper configuration into the TCCR1B register:

TCCR1B = (1<<CS12)|(1<<CS10);

In your program, you used |= instead of =. This doesn't write the bits appropriately: it only sets (writes to 1) the bits CS12 and CS10 but doesn't clear CS11. Since the Arduino core library sets the prescaler to 64 (CS11 and CS10 set), you end up with all the clock-select bits sets, in which case the timer is clocked by an external signal on the T1 pin. Not what you want.


Edit: Judging from the comments, it appears that at least dannyf did not understand my answer, I am therefore adding a clarification. There are two scenarios when you may want to tweak the control registers of a peripheral:

  1. The peripheral is normally controlled by the Arduino core library, and you want to use it through that library. Typical case: you want to use a timer to analogWrite(), or the ADC to analogRead(), but you want to clock the peripheral at a frequency which is not the core's default.

  2. You want to use a peripheral directly rather than through the Arduino core. Either you are going to “take it over” from the core, or it's a peripheral the core doesn't use in the first place.

In the first case, you should only touch those bits of the control registers that you must change. The other bits should be left alone, at whatever values the core has set them to. This is the approach of dannyf's answer.

In the second case, it is wiser to not rely on the core's default setting, and instead write every single bit of the control registers to the values appropriate for your application. This is the scenario of the original question, and the solution proposed in my answer.

For the particular case of this question, the TCCR1B register would be set to: ICNC1 = 0, ICES1 = 0, reserved bit = 0, WGM13 = 0, WGM12 = 0, CS12 = 1, CS11 = 0 and CS10 = 1. Combining those bits together gives the setting TCCR1B = 0x05;. However, for readability reasons, I prefer to write that value (1<<CS12)|(1<<CS10), as in the question and in my original answer. Some people may prefer to explicitly write every bit, like

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

Whichever version you prefer is up to you. I personally don't find this latter version any easier to read.

: The values of ICNC1 and ICES1 are actually irrelevant in this case.

5
  • Thanks it solved my problem. i have just checked the default value of TCCR1B . it is 0x33 . But in the datasheet default value of TCCR1B = 0x00. i was expecting the value of CS11 bit to be 0. Aug 18, 2017 at 15:33
  • " i have just checked the default value of TCCR1B . it is 0x33 ." that means the code is setting WGM13. With the proposed "beginner" approach, WGM13 is cleared. To the extent that your code uses anything that requires WGM13 being set, like PWM, it wouldn't work.
    – dannyf
    Aug 18, 2017 at 16:54
  • "But in the datasheet default value of TCCR1B = 0x00. i was expecting the value of CS11 bit to be 0." you should never rely on the default values. and your code should only change the registers / bits to the extent that you need. For example, if you want to change prescalers, change only those registers / bits that are related to the registers / bits that need to be changed. Otherwise, your code may have unexpected impact on other parts of your code.
    – dannyf
    Aug 18, 2017 at 16:56
  • It is because of that, the code proposed to you is very dangerous: it is a bomb that will explode in your face when the conditions are "unexpectedly" right.
    – dannyf
    Aug 18, 2017 at 16:57
  • @parasbhanot: “But in the datasheet default value of TCCR1B = 0x00”. This only applies after a reset. You can rely on this default if you do not use a bootloader nor the Arduino core. The Uno's bootloader does its best to leave every register at its reset state, but it could be unwise to rely on this. And if you use the main() from Arduino core (the “normal” Arduino practice, where you write setup() and loop() but not your own main()), then many registers will end up in the Arduino's default setting, which is not the same as the hardware-defined default. Aug 18, 2017 at 19:32
0
// set the pre-scaler to 1024 (slowest)

TCCR1B |= (1<<CS12)|(1<<CS10);

that approach presumes for the CS11 bit. in this case, if CS11 is set, the clock source is routed to the CKI pin, and if you don't apply a clock to that pin, the timer will never overflow.

Two options:

1) do it via two steps: set CS12/CS10 and clear CS11; or 2) do it in one step:

TCCR1B = (TCCR1B &~0x07) | (ps & 0x07);

ps would be whatever prescaler setting you wish to use.

the proposed solution below,

TCCR1B = (1<<CS12)|(1<<CS10);

is a typical beginner mistake, and a very dangerous one, as it clears other TCCR1B bits without the programmer even realizing it.

1
  • 1
    TCCR1B = (1<<CS12)|(1<<CS10); is a typical beginner mistake.” No, it's the right thing to do. If you are configuring a timer for your own usage, you should not rely on whatever settings the Arduino core left in the control registers. Instead, you should set every bit of the control register to the value appropriate for your application. You could do that bit by bit, if you enjoy useless complexity, but the simplest solution is just control_register = the_value_I_want;. Aug 18, 2017 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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