Not able to read 16bit Counter value

Hi,
I’m struggling with a simple problem trying to read a 16 bit counter value. Using 2560
counter 5.

I have an interrupt driven timer with a view to measure the input period of an external
signal. The interrupt seems to work and yet when I read TCNT5 (saved as an unsigned int
called inputPeriod) and then attempt to print it it invariably, despite multiple strategies
to achieve it, the upper byte of TCNT5 is missing.

The frequency of the input signal should result in 16 bit values with non-zero upper bytes.

Craig

Code.txt (458 Bytes)

OP’s code where we can see it:

const byte interruptPin=2; 
unsigned int inputPeriod;
void setup() {
 pinMode(interruptPin,INPUT_PULLUP);
 pinMode(outputPin,OUTPUT);
 attachInterrupt(digitalPinToInterrupt(interruptPin), inputTimer, FALLING);
 TCCR3B = (1<<CS50) | (1<<CS51)| (0<<CS52); //set the pre-scalar as 64
 TCNT5=0x0000;
 Serial.begin(9600);
 interrupts(); 
}

void loop() {
  Serial.println(inputPeriod,HEX);
}
void inputTimer(){
inputPeriod=TCNT5;
TCNT5=0x0000;
}

inputPeriod needs to be volatile if it is going to be used in the ISR and out. Otherwise the compiler just looks at loop and says, "inputPeriod never changes here, I'm just going to print 0 every time then" and optimizes away your variable.

Also, why are printing 'inputPeriod' every time loop() executes? Wouldn't it be better to only print it after every timer interrupt when you have a new value to print?

Also, your access to 16-bit 'inputPeriod' variable in loop will be non-atomic on an 8-bit AVR. That will cause problems. You need to protect said accesses with critical sections.

TCCR3B = (1<<CS50) | (1<<CS51)| (0<<CS52); //set the pre-scalar as 64

Trying to set the external clock source on Timer5 with the control register for Timer3 is wrong.

Why have you left an obviously incorrect comment attached to that line?

Hi,
thanks for your reply. I think I understand you line of reasoning. I added the volatile
qualifier but the serial monitor still produces one byte only.

You can see in the following code that I, in the ISR, defined inputPeriod =0xddff
and then printed (serial monitor) it and it duly printed all four characters.
I tends to make me think that the variable inputPeriod is OK but TCNT5 is only
populating the lower byte of inputPeriod.

const byte interruptPin=2;
volatile unsigned int inputPeriod;
void setup() {
pinMode(interruptPin,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), inputTimer, FALLING);
TCCR5B = (1<<CS50) | (1<<CS51)| (0<<CS52); //set the pre-scalar as 64
TCNT5=0x0000;
Serial.begin(9600);
interrupts();
}

void loop() {
Serial.println(inputPeriod,HEX);
}

void inputTimer(){
inputPeriod=TCNT5;
inputPeriod=0xddff;
TCNT5=0x0000;
}

Craig

gfvalvo:
Also, your access to 16-bit 'inputPeriod' variable in loop will be non-atomic on an 8-bit AVR. That will cause problems. You need to protect said accesses with critical sections.

What is supplying the external clock source pulse? What frequency? What pin is it attached to?

What is triggering the external interrupt on pin 2. How frequently do you trigger?

Hi,

Also, why are printing 'inputPeriod' every time loop() executes?

This is just for testing purposes. I was trying to establish whether my variable
inputPeriod was valid. At the current time it has only the lower byte populated.

trying to set the external clock source on Timer5 with the control register for Timer3 is wrong.

You are crrect, its a typo. I had in the course of my experimentation tried timers 1, 3 and 5,
thinking that maybe I was demanding a 16bit number from a 8 bit timer.

Also, your access to 16-bit 'inputPeriod' variable in loop will be non-atomic on an 8-bit AVR. That will cause problems.

I took this from the 2560 manual:

unsigned int i;
...
/* Set TCNTn to 0x01FF /
TCNTn = 0x1FF;
/
Read TCNTn into i */
i = TCNTn;

Thus when using C the underlying machine code does the two stage read/write. I agree that
should an interrupt ocurr it could screw with my measurement. This is a detail for consideration
after I've solved this impasse.

Craig

Here is an external clock source example written for Timer1 on UNO. It may give you some ideas on how to do this. The pulse source is tone() on pin 11 jumpered to pin5. Period is gated by a millis() timer. Pin numbers may need to change for a Mega.

unsigned long count_period = 2000;
unsigned long final_counts;
unsigned long start_time = millis();
unsigned long measured_time;
unsigned long current_time = millis();

void setup()
{
  Serial.begin(115200);
  Serial.println("Starting external clock source test");
  TCCR1A = 0; //initialize Timer1
  TCCR1B = 0;
  TCNT1 = 0;
  pinMode( 5, INPUT_PULLUP); //external source pin for timer1
  //set external clock source pin D5 rising edge
  TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12);

  //Test pulses to confirm circuit 5000 pps
  pinMode(11, OUTPUT);
  tone(11, 5000);//test pulse jumper pin 11 to pin 5
}

void loop()
{
  current_time = millis();
  if (current_time - start_time >= count_period)
  {
    TCCR1B = 0; //stop counter
    final_counts = TCNT1; //frequency limited by unsigned int TCNT1 without rollover counts
    TCNT1 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12); //restart external clock source

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.println(final_counts);
  }
}

Hi,

What is supplying the external clock source pulse? What frequency? What pin is it attached to?

What is triggering the external interrupt on pin 2. How frequently do you trigger?

Thanks for your interest. At this stage of the development pin 2, the external interrupt pin,
is hooked to a signal generator. The range of frequencies is 3Hz to 300Hz.

During the course of experimentation I have the Serial.print statement in the ISR. At low frequencies the
number of writes to the serial monitor slowed down and conversely they sped up at high
frequencies. This leads me to believe that the the ISR is working.

Craig

At this stage of the development pin 2, the external interrupt pin,
is hooked to a signal generator. The range of frequencies is 3Hz to 300Hz.

What is connected to the external clock source input T5 on pin 47?

joeaverage:
I took this from the 2560 manual:

Thus when using C the underlying machine code does the two stage read/write. I agree that
should an interrupt ocurr it could screw with my measurement. This is a detail for consideration
after I've solved this impasse.

Craig

That would be fine if your code in loop was reading the counter register. It's not. It's reading the inputPeriod variable. That access is not atomic. You need a critical section for that.

void loop() {
  cli();
  unsigned int copy = inputPeriod;
  sei();
  Serial.println(copy,HEX);
}

Hi,
again as a part of my experimentation I tried this:

Serial.println(TCNT5,HEX);

both in the ISR and the main loop with still the lowest byte only being written.

I also tried:
Serial.print(TCNT5H,HEX);
Serial.println(TCNT5L,HEX);

with the same result. I tried:

Serial.print(highByte(TCNT5),HEX);
Serial.println(lowByte(TCNT5),HEX):

I would swear that TCNT5 is an 8 bit only register!

Craig

Hi,
I am relying on an include file which I cannot view.

Is there a specific file or location that I can view it? No matter what I have tried TCNT5
behaves if it were one byte only.

Craig

Timer5 on a Mega is 16 bit according to the data sheet.

Are you using the Arduino IDE?

The correct reading of the High and Low bytes of the 16 bit Timer registers is taken care of by the compiler.

Hi,

Timer5 on a Mega is 16 bit according to the data sheet.

That’s my understanding also.

Are you using the Arduino IDE?

The correct reading of the High and Low bytes of the 16 bit Timer registers is taken care of by the compiler.

Yes, I am using Arduino IDE, and yes I anticipated that 16bit reads/writes would be accommodated
without me have to write assembler.

I tried this (in the ISR):

void inputTimer(){
TCNT5=0xdf20;
Serial.println(TCNT5,HEX);
}

And the output is attached.

It writes the lowest byte (20) but not the highest byte (df)???

Craig

Serial.print in an ISR is a bug since Serial is interrupt driven. If it doesn't work with Serial in the interrupt that doesn't really mean a lot.

Have you tried making a copy in an critical section like in #12?

TCNT5 is probably defined as or is at least being treated as a volatile byte pointer. If so, then when you call print it may be trying to treat it as a 8 bit variable. print isn't very smart sometimes. Try printing the signed int (0xF0FF) in hex without any casting and see what happens. IIRC it will promote to 32 bit and sign extend and end up printing 0xFFFFF0FF

BY making a copy in a variable and printing in that variable things may be nicer, but you have to remember that the variable access isn't atomic.

Hi,
if I initialize inputPeriod, an unsigned int, to 0xffda say, and print the output has two bytes, ie four
characters. If I initialize TCNT5 to the same 0xffda and print it will output only the lower byte, namely
'da'.

It seems TCNCT5 is being treated as a one byte variable.

Where do I find the includes file?

Craig

joeaverage:
Where do I find the includes file?

In the core. In the folder where you installed Arduino. Grep the files for TCNT5 and pick the one that applies to you.

Have you still not tried it the way I suggested with the cli and sei instructions and printing from the variable and not the counter register? Then it wouldn’t matter what overload of print was being used for TCNT5.