Initializing timer registers on 16bit counter?

Hi,

i have an Arduino board with an atmega328 and i use the arduino software version 17.
To generate a normal timer interrupt i tried to initialize the OCR1A register with a 16-bit hex-value for the 16-bit timer.

OCR1A = 0x0ABD;

My program didn't work right, so i made a check. I found out that the problem is that when i requested the value from the register

servoByteH = (int)servoPulse>>8; // the high byte of OCR1A
servoByteL = (int)255&servoPulse; // the low byte of OCR1A
Serial.println(servoByteH);
Serial.println(servoByteL);

The high-byte called OCR1AH was totally empty!

So is there a problem with the arduino software not being able to write into 16bit registers? Or what did i do wrong?
I know there's a TEMP register used for the high-byte to write into OCR1A simultaneously. But even though i loaded the <avr/interrupt.h> and the <avr/io.h> he doesn't know the expression TEMP.

Can you please help me how to access the 16bit register? Thanks!

Greets, ThunderBird

From this code snippet, we can't see how servoPulse is defined or valued. Can you show a bit more of your code?

This is my code:

#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdlib.h>
#include <stdint.h>
#include "Wire.h"
#include "WiiChuck.h"

// ------------------ variable definitions ------------------------

uint16_t min16 = 540;//540; // = 540uS min. pulse width - prescaler 8
uint16_t max16 = 2400; // = 2400uS min. pulse width - prescaler 8
int servoPulse;
uint8_t angle; // in degrees
boolean zButtonPressed = false;
boolean cButtonPressed = false;

WiiChuck chuck = WiiChuck();

void setup() {

TCCR1A |=(1<<WGM13)|(1<<COM1A1); // using TIMER1, setting mode: PWM - phase and frequency correct mode

TCCR1B |=(1<<CS11); // defining the prescaler 8 --> f_cpu = prescaler*f_timer

DDRB |= (1<<DDB1); // setting Port B as output for PWM

ICR1 = 0x5DC0; // period length 24ms with prescaler 8 matches ICR1 = 24000
OCR1A = 0x0000; // reseting PWM register
pinMode(PORTB, OUTPUT); // output for servo

chuck.begin(); // initialising nunchuck
chuck.update();
Serial.begin(9600); // debug serial output
}

void calcPulseWidth(uint16_t angleArg)
{
unsigned long help = 0;
if ( angleArg < 0) angleArg = 0;
if ( angleArg > 180) angleArg = 180;
angle = angleArg;

help = ((long)(max16-min16))*((long)angle);
servoPulse = (min16 + (uint16_t)(help/180L));
}

void loop()
{
chuck.update();
zButtonPressed = chuck.buttonZ;
cButtonPressed = chuck.buttonC;

if(zButtonPressed == true)
{
calcPulseWidth(180); // setting new servo pulse
OCR1A = servoPulse;
Serial.print("180°: ");
Serial.println((int)OCR1A);
}
else
{
calcPulseWidth(0); // setting new servo pulse
OCR1A = servoPulse;
Serial.print("0°: ");
Serial.println((int)OCR1A);
}
}

As you can see i tried to read what i wrote in the OCR1A register again and as it seems he only accesses the lower 8-bit of the OCR1A.

My problem is actually that i don't really know how to access the 16-bit registers.
And how can i check whether the compiler understands operations like:
int a = 1000;
OCR1A = a;
Because in assembler you have to put your high-byte first in the TEMP register and then after that when you load the low-byte into the OCR1A the high-byte gets into the OCR1A automatically.
But i don't know if this background stuff is all done by the compiler.

I have a very similar problem as well.

At first I thought it was the high/low/temp register order problem as described here. It should have been fixed for long.

I've checked that this code for example:

TCNT1 = 0xFFAA;

is turned into these assembler commands:

  TCNT1 = 0xFFAA;
  da:   e4 e8           ldi     r30, 0x84       ; 132
  dc:   f0 e0           ldi     r31, 0x00       ; 0
  de:   2a ea           ldi     r18, 0xAA       ; 170
  e0:   3f ef           ldi     r19, 0xFF       ; 255
  e2:   31 83           std     Z+1, r19        ; 0x01 (store HIGH byte first)
  e4:   20 83           st      Z, r18             ; (store LOW byte last)

And reading:

unsigned int timer1;
timer1 = TCNT1;

  timer1 = TCNT1;
  e6:   20 81           ld      r18, Z             ; (read LOW byte first)
  e8:   31 81           ldd     r19, Z+1        ; 0x01 (read HIGH byte last)
  ea:   3a 83           std     Y+2, r19        ; 0x02
  ec:   29 83           std     Y+1, r18        ; 0x01

This is OK according to the datasheet.

Still when I want to use e.g. high_of(timer1), it is always 0! Something like:

Serial.println((timer1>>8)&0xFF,HEX);

This may be interesting as well:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82847&highlight=tcnt1h

Edit: well only a little. Not much enlightenment there.

I've done some further checks with LEDs as indicators of HIGH/LOW bytes in a 16bit variable (unsigned int). I've also compiled it outside the Arduino IDE and the behaviour is the same. Still the assembler code looks ok as far as the order of reads/writes is concerned.

It boils down to this:

First I stop TIMER1 by setting the prescaler accordingly. Then it let the compiler write 0xFFFF into TCNT1 (16bit reg), assuming it knows how to do it correctly and instantly read it back into an uint16_t variable called timer1. I use 8 LEDs to show the LOW/HIGH byte contents of timer1 and print the results to serial as well.

The LOW byte content of timer1 is correct (0xFF) and 8 LEDs light up. The HIGH byte though is ZERO. If i replace the readout of TCNT1 into timer1 with just "timer1 = 0xFFFF", LOW/HIGH byte content of timer1 is correct and the 8 LEDs light up twice. The serial output is correct as well in this case.

So apparently reading TCTN1 into timer1 fails. Manually reading LOW/HIGH of TCNT1 using TCNT1L and then TCNT1H fails as well.

#include <util/delay.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

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

  DDRD |= ((1 << PD5));            // PD5 output
  PORTD |= ((1 << PD5));      // RED anodes HIGH. All 8 anodes go to this pin
  DDRB = 0xFF;                  // all outputs
  PORTB = 0xFF;                  // all 8 cathodes HIGH --> OFF
}

void
loop (void)
{

  unsigned char sreg;
  volatile unsigned int timer1;
  unsigned char ctr;

  sreg = SREG;                  // store IRQ flags
  cli ();                  // all IRQs off
  TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));      // stop timer1
  TCNT1 = 0xFFFF;            // set arbitrary value, should stay the same as timer1 is stopped
  timer1 = TCNT1;            // read it back. the compiler should know how to do a 16bit read
  //timer1 = 0xFFFF;            // use this instead of reading TCNT1 and it works as expected
  SREG = sreg;                  // restore IRQ flags

  for (ctr = 0; ctr <= 7; ctr++)
    {                        // show LOW byte of timer1
      if ((timer1 >> ctr) & 0x0001)
      {
        PORTB &= ~(1 << ctr);      // turn the LED on
      }
      else
      {
        PORTB |= (1 << ctr);      // turn the LED off
      }
    }
  _delay_ms (300);
  PORTB = 0xFF;                  // all off
  _delay_ms (300);

  for (ctr = 0; ctr <= 7; ctr++)
    {                        // show HIGH byte of timer1
      if ((timer1 >> (ctr + 8)) & 0x0001)
      {
        PORTB &= ~(1 << ctr);      // turn the LED on
      }
      else
      {
        PORTB |= (1 << ctr);      // turn the LED off
      }
    }
  _delay_ms (300);
  PORTB = 0xFF;                  // all off

  Serial.print ("timer1 (should be FFFF) : ");
  Serial.println (timer1, HEX);
  Serial.print ("timer1>>8 & 0xFF (high byte, should be FF): ");
  Serial.println ((timer1 >> 8) & 0xFF, HEX);
  Serial.print ("timer1 & 0xFF (low byte should be FF): ");
  Serial.println (timer1 & 0xFF, HEX);
  Serial.println ("-");
  delay (3000);
}

What the @$& is going on ?

BTW,

Happy Holidays everybody.

I've started a thread on avrfreaks.net with basically the same question. Maybe some of the experts there know what the problem is.

Hi madworm,

I see what you are trying to do but that test does not duplicate real world use of the timer. Have you checked the datasheet to see if reading TCNT1 is valid when the timer is not running?

Perhaps try a test with the timer running and set to the slowest count possible and poll TCNT1 as follows :

void setup()
{
    TCCR1A = 0;             // normal counting mode 
    TCCR1B = _BV(CS10) |  _BV(CS12);      // set prescale to 1024
    TCNT1 = 0;              // clear the timer count 
    Serial.begin(9600);
}

void loop()
{
  sreg = SREG;                  // store IRQ flags
  cli ();                  // all IRQs off
  timer1 = TCNT1;            // read it back
  SREG = sreg;                  // restore IRQ flags
  Serial.print(millis());
  Serial.print(","); 
  Serial.println(timer1); 
}

You should see timer1 increase by however many ticks of 64us it takes to do the loop. You should see the timer value and the millis value correspond (multiply the timer value by .064 to get milliseconds)

Yes it is valid.

I plugged my test code into some other piece of code I have, which also uses timer1, and it works there. It precharges timer1 to some value very close to 0xFFFF to increase the frequency of the overflow interrupt.

What is the difference ? As you may very well already know, timer1 can be a 16bit counter, but doesn't have to be. Anyhow, it only works as it should when the WGM10 bit of TCCR1A is zero.

Now the question arises is why WGM10 is not 0, although the datasheet claims the default value for TCCR1A is 0.

Anyhow, it only works as it should when WGM10 = 0.

For normal counting mode, you need WGM10 set to 0.

Now the question arises is why WGM10 is not 0, although the datasheet claims the default value for TCCR1A is 0.

Its not in the default normal counting mode because the Arduino startup code initializes the timers for PWM. If you want a particular mode you should always explicitly set all relevant registers.

Ungh. In this case the arduino security blanket sucks.

Is there a way to selectively disable arduino glue when not needed ? If all the analogWrite stuff is never called at all for example, don't mess with the timer registers ? But there's also delay and so on. Maybe it's time to move on. I guess the arduino ide just can't provide a more open way of "include this, configure that, but exclude ...". Maybe an 'advanced mode' with e.g. a PWM.h include for the timers and so on. If the core were split up like that, more experienced users could decide what they really need.

Ungh. In this case the arduino security blanket sucks.

I think the problem may be more with your expectations than with Arduino :wink:
Saying it sucks because there is no simple way to cut holes in the security blanket is missing the point of Arduino - its purpose is the security blanket.

I wouldn't disagree that Arduino hardware initialization would benefit from being more flexible, but supporting more boards would IMO be a higher priority than disabling Arduino functionality for experienced programmers.

Anyway, there is nothing stopping you from modifying the initialization code in wiring.c to make it do what you want. And if you create a board variant in the hardware/cores directory for the hacked core, you can switch between the standard version and your customized core as needed.

Have fun

I am trying to set the TTCR1B resister and get a strange result

void setup()
{
char s[4];
Serial.begin(9600);

TCCR1B |= (0 << CS12)|(1 << CS11)|(0 << CS10); // Should clear bit 0 and 2 and set bit 1.

Serial.println("=======================");
snprintf(s, 4, "%d", TCCR1B);
Serial.println(s); // (The result I get is a decimal 3 insted of a decimal 2)
Serial.print("=======================");

}

void loop()
{

}

The expected result is a 2 and I get 3