Problem reading/writing 16-bit Timer1 registers

In trying to understand how to use the 16-bit Timer1 correctly, I have run into a problem that must be very simple but is unyielding to all of my attempts to tease out a solution.

I want to write OCR1A and OCR1B to values greater than 256 but following the suggestions on p152/3 in the Atmel Datasheet I can't seem to get the results promised.

There is a note toward the bottom of p152 that says "1. The example code assumes that the part specific header file is included." and I don't know what a "part specific header file" is or where I would look for it. I have been assuming (perhaps incorrectly) that the IDE includes the appropriate set of #defines and libraries that are required for support of the language.

Here is the sketch I am using - it runs for a little over 2.5 sec (dominated by the delay statements):

char cSREG;//SREG storage
unsigned long int ISRmicrotimerCOMPA;//interrupt time tag
unsigned long int ISRmicrotimerCOMPB;//interrupt time tag
unsigned long int TempUI;

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

  cSREG = SREG; //Store Status Register
  cli();//stop interrupts before messing with internal registers
  TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));// turn Timer1 off
  TIM16_WriteTCNT1(257);//try to initialize TCNT1 to a value >255;
  sei();
  SREG = cSREG; //enable interrupts to print an intermediate result
  Serial.print("TCNT1:#1 \t"); Serial.println(TIM16_ReadTCNT1());//reads only the LOW byte
  cSREG = SREG; //Store Status Register
  cli();
  //set compare match registers
  OCR1A = 2000;//try to set OCR1A and OCR1B but it only loads the LOW byte
  OCR1B = 1000;
  TIMSK1 = (1 << OCIE1B) | (1 << OCIE1A);//enable both compare/match A & B
  TCCR1B = 5;//set prescaler to 1024
  sei();//reenable interrupts to allow delay to put some more counts on TCNT1
  SREG = cSREG;
  delay(1000);
  cSREG = SREG;
  cli();
  TempUI = TIM16_ReadTCNT1();//try to read TCNT1 using the function suggested in the Datasheet
  sei();
  SREG = cSREG;
  delay(1000);//allow TCNT1 to count for one more second which should yield TCNT1 of ~ 32000
  Serial.print("TempUI:TCNT1 #2 \t"); Serial.println(TempUI);
  Serial.print("TCNT1:#3 \t"); Serial.println(TIM16_ReadTCNT1());
  Serial.print("TCCR1B: \t"); Serial.println(TCCR1B, BIN);
  Serial.print("TIMSK1: \t"); Serial.println(TIMSK1, BIN);
  Serial.print("OCR1A: \t"); Serial.println(OCR1A);
  Serial.print("OCR1B: \t"); Serial.println(OCR1B);
  Serial.print("micros at ISR for OCR1A: \t"); Serial.println(ISRmicrotimerCOMPA);
  Serial.print("micros at ISR for OCR1B: \t"); Serial.println(ISRmicrotimerCOMPB);

}

void loop() {//put your stuff here
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
unsigned int TIM16_ReadTCNT1(void) {//safely reads TCNT1
  unsigned char sreg;
  unsigned int i;
  sreg = SREG;
  cli();
  i = TCNT1;
  SREG = sreg;
  return i;
  sei();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void TIM16_WriteTCNT1 (unsigned int i)
{ //safely writes TCNT1
  unsigned char sreg;
  /* Save global interrupt flag */
  sreg = SREG;
  /*Disable interrupts */
  cli();
  /* Set TCNT1 to i */
  TCNT1 = i;
  /*Reenable interrupts */
  sei();
  /* Restore global interrupt flag */
  SREG = sreg;
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
ISR(TIMER1_COMPA_vect) { //when TIMER1.TCNT1 == OCR1A
  ISRmicrotimerCOMPA = micros();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
ISR(TIMER1_COMPB_vect) {
  ISRmicrotimerCOMPB = micros();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

The resulting output:
TCNT1:#1 1 (can set TCNT1 to a small number)
TempUI:TCNT1 #2 184 (but after it has been running for a while it still shows less than 256 ticks)
TCNT1:#3 147 (and even later it has wrapped around again
TCCR1B: 101 (prescaler set to 1024)
TIMSK1: 110 (both match /compare flags are set)
OCR1A: 208 (= 2000 % 256) can't tell if it was loaded correctly or if I am just reading the LOW byte
OCR1B: 232 (= 1000 % 256)
micros at ISR for OCR1A: 2543168 (so the interrupt has been running successfully in the background)
micros at ISR for OCR1B: 2574272 (the 25ms difference is ~ the Serial.print latency)


What am I missing?

Perhaps someone could post a sketch that demonstrates how to write and read 16-bit values to and from TCNT1, OCR1A, and OCR1B.

I found a solution!

While I do not understand why it works,
I set WGM13 in TCCR1B to 1:

TCCR1B |= (1<< WGM13);

and after doing that everything works.
I can write and read all of the 16-bit registers directly in C without dealing separately with the High and Low bytes. I can write and read them when Timer1 is off and when Timer1 is running

This seems arcane in the extreme.
Does anybody know where it is documented?
It seems really important yet mysteriously opaque.
Could it be a compiler bug? Or for some reason a "feature" of value under some special circumstances.

You are carrying over presets from the Arduino ide which puts you in PWM to 255 (TCCR1A = 1) and the upper byte register is not accessed.

It is always best to remove any ide modal presets with TCCR1A =0 and TCCR1B = 0 before setting registers as you want.

char cSREG;//SREG storage
unsigned long int ISRmicrotimerCOMPA;//interrupt time tag
unsigned long int ISRmicrotimerCOMPB;//interrupt time tag
unsigned long int TempUI;

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

  cSREG = SREG; //Store Status Register
  cli();//stop interrupts before messing with internal registers
  TCCR1A = 0;
  TCCR1B =0;
  //TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));// turn Timer1 off
  TIM16_WriteTCNT1(257);//try to initialize TCNT1 to a value >255;
  sei();
  SREG = cSREG; //enable interrupts to print an intermediate result
  Serial.print("TCNT1:#1 \t"); Serial.println(TIM16_ReadTCNT1());//reads only the LOW byte
  cSREG = SREG; //Store Status Register
  cli();
  //set compare match registers
  OCR1A = 2000;//try to set OCR1A and OCR1B but it only loads the LOW byte
  OCR1B = 1000;
  TIMSK1 = (1 << OCIE1B) | (1 << OCIE1A);//enable both compare/match A & B
  TCCR1B = 5;//set prescaler to 1024
  sei();//reenable interrupts to allow delay to put some more counts on TCNT1
  SREG = cSREG;
  delay(1000);
  cSREG = SREG;
  cli();
  TempUI = TIM16_ReadTCNT1();//try to read TCNT1 using the function suggested in the Datasheet
  sei();
  SREG = cSREG;
  delay(1000);//allow TCNT1 to count for one more second which should yield TCNT1 of ~ 32000
  Serial.print("TempUI:TCNT1 #2 \t"); Serial.println(TempUI);
  Serial.print("TCNT1:#3 \t"); Serial.println(TIM16_ReadTCNT1());
  Serial.print("TCCR1B: \t"); Serial.println(TCCR1B, BIN);
  Serial.print("TIMSK1: \t"); Serial.println(TIMSK1, BIN);
  Serial.print("OCR1A: \t"); Serial.println(OCR1A);
  Serial.print("OCR1B: \t"); Serial.println(OCR1B);
  Serial.print("micros at ISR for OCR1A: \t"); Serial.println(ISRmicrotimerCOMPA);
  Serial.print("micros at ISR for OCR1B: \t"); Serial.println(ISRmicrotimerCOMPB);
}

void loop() {//put your stuff here
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
unsigned int TIM16_ReadTCNT1(void) {//safely reads TCNT1
  unsigned char sreg;
  unsigned int i;
  sreg = SREG;
  cli();
  i = TCNT1;
  SREG = sreg;
  return i;
  sei();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void TIM16_WriteTCNT1 (unsigned int i)
{ //safely writes TCNT1
  unsigned char sreg;
  /* Save global interrupt flag */
  sreg = SREG;
  /*Disable interrupts */
  cli();
  /* Set TCNT1 to i */
  TCNT1 = i;
  /*Reenable interrupts */
  sei();
  /* Restore global interrupt flag */
  SREG = sreg;
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
ISR(TIMER1_COMPA_vect) { //when TIMER1.TCNT1 == OCR1A
  ISRmicrotimerCOMPA = micros();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
ISR(TIMER1_COMPB_vect) {
  ISRmicrotimerCOMPB = micros();
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
TCNT1:#1 	257
TempUI:TCNT1 #2 	15882
TCNT1:#3 	31515
TCCR1B: 	101
TIMSK1: 	110
OCR1A: 	2000
OCR1B: 	1000
micros at ISR for OCR1A: 	611840
micros at ISR for OCR1B: 	547840
1 Like

@cattledog Thank you.

Is there anyplace where all of the IDE presets are listed?
It makes sense that there would be some but my incorrect assumption was that bits were zero unless specifically set. Obviously a bad assumption. I will try to remember the lesson and be more careful in the future.

Is there anyplace where all of the IDE presets are listed?

The timer presets for pwm are implemented in wiring.c. In windows, the path is

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\wiring.c

In a section called init(), which is called behind the scenes before setup(), you will find

// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
	sbi(TCCR1A, WGM10);

I'm not exactly certain where in either the ide or the compiler the upper registers are blocked out in this mode.

From Atmel's point of view, the ATMega chip is the part. It has a part number like 328p. (The datasheet will list all the part numbers that are covered by that datasheet, how to order them and even what type of box they come in.)

The part-specific header file sets up the names like TCCR1B for you. That's already included in the Arduino system that's largely hidden from you. If you switch to a different Arduino (eg to a Leonardo, which has a 328u4 chip) then that part's specific header file is now included.

Where is the specific part identified or how is it discovered? I don't recall having included that detail in any header or tailoring file that became part of the sketch. How does it know that I was programming for an Uno vs a Mega? So that if I try to use more interrupts than the part supports, it seems like there there should be a compile error rather than just a runtime error.

How does it know that I was programming for an Uno vs a Mega?

In the ide the menu pulldown tools>board selection establishes the correct core for the compiler to use.

Of course. Had to do that on day one. Then promptly forgot that it was ever done. Thanks for your patience.

DocDough:
Is there anyplace where all of the IDE presets are listed?

It is best to assume that the timer registers are set to the worst possible settings for your use and initialize everything:

  noInterrupts();
  TCCR1A = 0; // Set the OCR pins to normal I/O and clear half of the WGM
  TCCR1B = 0; // Stop the timer clock and clear other stuff
  TIMSK1 = 0; // Clear all interrupt enable mask bits
  TIFR1 = 0xFF; // Clear any pending interrupt flag (Yes, write a '1' to CLEAR a flag.  Saves read/modify/write cycles.)
  interrupts();  // Nothing will happen until TCCR1B is written