Timer/Counter Configuration Oddity in ArduinoIDE

Say, for example, one wants to output a 4MHz clock on an Uno, they might write the following:

pinMode(11, OUTPUT);
OCR2A = 1;
TCCR2A = bit(COM2A0) | bit(WGM21);
TCCR2B = bit(CS20);

But this code results in an 8MHz output; evidently this is because setting TCCRnA also sets OCRnx to 0. However, this is only the case when written in the ArduinoIDE as the same instructions in the same order produce the expected output when written in AVR_Sim. The IDE is adding extra activity to this instruction, requiring the code to set OCRnx after TCCRnA, but I didn't see it documented anywhere.

Why does setting TCCRnA in the ArduinoIDE do this? Is it from the IDE itself, the compiler, or something else? Is it intentional (and if so, is it documented)?

Relevant points of note:

  • setting TCCRnA through __asm__() also sets OCRnx to 0
  • setting other T/C configuration registers like TCCRnB does not affect OCRnx
  • this was tested on multiple Unos and a Mega with T/Cs 1 & 2 using IDE 1.8.19

Edit to add:
I found after posting this post: Timer0 issue. OCR0A not working, which initially has the same code order, but the poster changed the order without really discussing why this is.

As your topic does not relate directly to the installation or operation of the IDE it has been moved to the Programming category of the forum

1 Like

Specifically, I am interested in knowing why and how this departure from a typical register assignment occurs in the ArduinoIDE.

If someone more knowledgeable in the inner machinations of the IDE could shed some light on this, I would very much appreciate it.

I don't see that when I run this code

void setup() {
Serial.begin(115200);
pinMode(11, OUTPUT);
OCR2A = 1;
TCCR2A = bit(COM2A0) | bit(WGM21);
TCCR2B = bit(CS20);
Serial,print("OCR2A value: " );
Serial.println(OCR2A, BIN);
}

void loop() { }

OUTPUT:
OCR2A value: 1

That seems extremely unlikely...
Please provide a complete program that demonstrates the problem, and perhaps one that does NOT demonstrate the problem.

The IDE is adding extra activity to this instruction, requiring the code to set OCRnx after TCCRnA

Where do you think you're seeing that?

TCCR2A = bit(COM2A0) | bit(WGM21);
TCCR2B = bit(CS20);

TCCR2B sets the clock source for the timer, right? Are you sure you don't need to do that before your change the timer mode? The core code DOES configure the timers ("Phase correct PWM" on timer2) in the initialization code, but doesn't connect them to pins or set the OCR register until you use analogWrite() or tone()

The full code of the program that I used to isolate the effect of TCCRnA is as follows, with variations showing when OCRnx work as expected and when they behave oddly. The loop function was always empty and is thus omitted from all code posted here.

The Working Configurations:

Using Normal Setting:

void setup()
{
  Serial.begin(38400);
  
  pinMode(11, OUTPUT);
  TCCR2A = bit(COM2A0) | bit(WGM21);
  OCR2A = 1;
  TCCR2B = bit(CS20);
  
  pinMode(10, OUTPUT);
  TCCR1A = bit(COM1B0);
  OCR1A = 249;
  OCR1B = 248;
  TCCR1B = bit(WGM12) | bit(CS10);
  
  Serial.println("Configured");
  Serial.print("OCR2A: ");
  Serial.println(OCR2A);
  Serial.print("OCR1A: ");
  Serial.println(OCR1A);
  Serial.print("OCR1B: ");
  Serial.println(OCR1B);
}

Serial Monitor:

Configured
OCR2A: 1
OCR1A: 249
OCR1B: 248

Scope Measurements:
31.9kHz from pin 11
4MHz from pin 10

Using asm:

void setup()
{
  Serial.begin(38400);
  
  pinMode(11, OUTPUT);
  __asm__("push r16\n\t ldi r16, 0b01000010\n\t sts 0xB0, r16\n\t pop r16");
  OCR2A = 1;
  TCCR2B = bit(CS20);
  
  pinMode(10, OUTPUT);
  __asm__("push r16\n\t ldi r16, 0b00010000\n\t sts 0x80, r16\n\t pop r16");
  OCR1A = 249;
  OCR1B = 248;
  TCCR1B = bit(WGM12) | bit(CS10);
  
  Serial.println("Configured");
  Serial.print("OCR2A: ");
  Serial.println(OCR2A);
  Serial.print("OCR1A: ");
  Serial.println(OCR1A);
  Serial.print("OCR1B: ");
  Serial.println(OCR1B);
}

Serial Monitor:

Configured
OCR2A: 1
OCR1A: 249
OCR1B: 248

Scope Measurements:
31.9kHz on pin 11
4MHz on pin 10

The Odd Configurations:

Using Normal Setting:

void setup()
{
  Serial.begin(38400);
  
  pinMode(11, OUTPUT);
  OCR2A = 1;
  TCCR2A = bit(COM2A0) | bit(WGM21);
  TCCR2B = bit(CS20);
  
  pinMode(10, OUTPUT);
  OCR1A = 249;
  OCR1B = 248;
  TCCR1A = bit(COM1B0);
  TCCR1B = bit(WGM12) | bit(CS10);
  
  Serial.println("Configured");
  Serial.print("OCR2A: ");
  Serial.println(OCR2A);
  Serial.print("OCR1A: ");
  Serial.println(OCR1A);
  Serial.print("OCR1B: ");
  Serial.println(OCR1B);
}

Serial Monitor:

Configured
OCR2A: 1
OCR1A: 0
OCR1B: 0

Scope Measurements:
8MHz on pin 11
8MHz on pin 10

Using asm:

void setup()
{
  Serial.begin(38400);
  
  pinMode(11, OUTPUT);
  OCR2A = 1;
  __asm__("push r16\n\t ldi r16, 0b01000010\n\t sts 0xB0, r16\n\t pop r16");
  TCCR2B = bit(CS20);
  
  pinMode(10, OUTPUT);
  OCR1A = 249;
  OCR1B = 248;
  __asm__("push r16\n\t ldi r16, 0b00010000\n\t sts 0x80, r16\n\t pop r16");
  TCCR1B = bit(WGM12) | bit(CS10);
  
  Serial.println("Configured");
  Serial.print("OCR2A: ");
  Serial.println(OCR2A);
  Serial.print("OCR1A: ");
  Serial.println(OCR1A);
  Serial.print("OCR1B: ");
  Serial.println(OCR1B);
}

Serial Monitor:

Configured
OCR2A: 1
OCR1A: 0
OCR1B: 0

Scope Measurements:
8MHz on pin 11
8MHz on pin 10

As you can hopefully see, in The Working Configurations, setting OCRnx after TCCRnA produced the output one would expect, but in The Odd Configurations, setting OCRnx before TCCRnA resulted in 8MHz outputs in all cases and is reflected in the Serial Monitor outputs for T/C 1.

I'm not sure why OCR2A is reporting 1, which is in contradiction to the physical output, but refer to the OCR1x as they are coherent with the physical measurements and show the values being set to 0 in The Odd Configurations.

I think (and therefore I am) seeing that in the fact that setting the registers in the 'odd' order in asm written outside the ArduinoIDE results in normal operation whereas the 'odd' order written in normal Arduino results in the oddity. This, therefore indicates that some point in the ArduinoIDE chain between the user hitting upload and it being flashed onto the chip is the cause (otherwise the asm would too). If you would like to see the AVR_Sim assembly code that shows this, I can provide that as well.

Yes, as shown in the assignment order in The Normal Configurations, setting TCCRnB last did not result in any oddities. I also tried moving the TCCRnB assignment to various points throughout the T/C configuration and never saw any changes. In fact, in most situations, one would only want to set the clock source after all the other registers have been set, lest the counter surpasses your CTC value before you set it and run away until overflow.

Is the initialization code added before the setup() function? If so, when the user's setup() function assigns values to these registers, it overwrites any ones in there previously, even if they are just written a few instructions ago (assuming simple assignment and not any compound bitwise assignment). If the initialization code went after the the setup() function, I would expect none of this to work at all. I thus believe that it is not the initialization code causing this.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.