Accessing TCNT1H

Edit: Note that reading TCNT1H in C may not work as expected. Instead read TCNT1 (the 16-bit version) which C knows how to do.

Uno using Arduino 1.6.7

Having Timer 1 auto-reload by counting up. At OCR1A match, I get the interrupt.
(OCR1A = 9000;)

ISR(TIMER1_COMPA_vect) Interrupt working great.

To minimize jitter on a sample I take during each interrupt, I tried each of these inside the ISR:

while (TCNT1H ==0){} // pause if not 256 or more into the T1 count HANGS ME UP!

while ((volatile unsigned char)TCNT1H ==0){} // also hangs

while (TCNT1 <256){} //pause if not 256 or more into the T1 count WORKS!

I can read TCNT1H into a variable, and will get the expected value. Why don’t the first two work? They seem faster, and they seem more immune to interrupts (although timer 1 interrupt is the only one I explicitly enabled. The two shields may have enabled more.)

The two shields may have enabled more.)

Hardware does not enable interrupts. Software does.

Using a while loop, to delay until something else happens, in an ISR is just plain wrong.

PaulS: Using a while loop, to delay until something else happens, in an ISR is just plain wrong.

I disagree.

My SPI-using display (OK, the software for the library that provides the functions for the display) has drawing operations. I use "SPI.usingInterrupt(255);" in the non-interrupt code to prevent the interrupt messing up the image by interrupting the SPI communications. Now if the display library were to play nice by not blocking interrupts for as long as it does, that would be nice. But it doesn't. I can limit how much I do at a time, but there is ambiguity of when the ISR gets to my analog read. The wait loop seems wasteful of time, but minimizing sample jitter if I can is useful to me. Waiting for up to almost 3% of the time in the ISR is more than useful to me -- not a waste. And for me not wrong.

You may wonder why I use (255) rather than the actual interrupt number. The reason for that is that I could not figure out what the "interrupt number" that the SPI.usingInterrupt() wants as its operand for the TIMER1_COMPA wants. I tried TIMER1_COMPA_vect. I tried 6. No joy. Display anomalies resulted. 256 works. I did not ask, but I would also like to know what an interruptNumber is as called for in https://www.arduino.cc/en/Reference/SPIusingInterrupt

If you had a better way to accomplish the job, I would like to hear it.

Maybe I provided distracting information. I could have said that TCNT1 is a 16-bit counter which is addressed as an int or unsigned int (distinction not important in this case). The high byte is addressable as TCNT1H and the low byte is addressable as TCNT1L. I wanted to test the high byte, and wait until that byte had rolled up to 1, but am only succeeding in testing the 16-bit register in the ISR.

I am not sure if this testing of TCNT1H is a quirk of C, or if it is quirk of the processor.

By now I fear I have rambled on, and muddied up the question.

To minimize jitter on a sample I take during each interrupt

What sort of a sample? Why not explain what you actually want to do?

Consult the ATMega data sheet on how to read 16 bit registers. Your program hangs because TCNT1 and many others are treated specially by the hardware. Not only is it poor programming practice, your approach does not even work.

16.3 Accessing 16-bit Registers The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR CPU via the 8-bit data bus. The 16-bit register must be byte accessed using two read or write operations. Each 16-bit timer has a single 8-bit register for temporary storing of the high byte of the 16-bit access. The same temporary register is shared between all 16-bit registers within each 16-bit timer. Accessing the low byte triggers the 16-bit read or write operation. When the low byte of a 16-bit register is written by the CPU, the high byte stored in the temporary register, and the low byte written are both copied into the 16-bit register in the same clock cycle. When the low byte of a 16-bit register is read by the CPU, the high byte of the 16-bit register is copied into the temporary register in the same clock cycle as the low byte is read.

jremington: What sort of a sample? Why not explain what you actually want to do?

I want to take an analog voltage reading with an SPI-controlled analog board at a regular interval. It works. I want to improve it.

jremington: Consult the ATMega data sheet on how to read 16 bit registers. Your program hangs because TCNT1 and many others are treated specially by the hardware. Not only is it poor programming practice, your approach does not even work.

Odd comments. Saying "your approach does not even work" seems to be trying to imply that you know what is going on.

I tried checking the resulting assembly code.

while (TCNT1 <256){} // pause if not 256 or more into the T1 count

which works,generates

.L7:
    .loc 1 15 0 discriminator 1
    lds r24,132
    lds r25,132+1
    cpi r24,-1
    cpc r25,__zero_reg__
    breq .L7
    brlo .L7

That code is hard for me to follow, but since that works as intended, that’s OK.

while (TCNT1H ==0){}// pause if not 256 (0x0100) or more into the T1 count

which should be equivalent IMO, generates this:

.L3:
    .loc 1 8 0 discriminator 1
    lds r24,133
    tst r24
    breq .L3

This code seems simpler. http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_TST.html even shows tst followed by breq in the example on that page. Unfortunately it does not work as I intended. What is intended is that the code pauses until timer1, which is counting up from zero, has rolled from 0x00ff (255) to 0x0100 (256).

http://www.atmel.com/images/atmel-8272-8-bit-avr-microcontroller-atmega164a_pa-324a_pa-644a_pa-1284_p_summary.pdf confirms the address of TCNT1H is 0x85 (133).

Unfortunately it does not work as I intended.

Of course not. I suggest to read the documentation I quoted, which explains why that does not work.

To address the overall problem of precisely timed measurements, you need to analyze all the possible sources of timing jitter in making measurements and reduce the worst of those first. For an SPI controlled external device, I am certain that the jitter in a timer interrupt is one of the less important contributions.

jremington:
Of course not. I suggest to read the documentation I quoted, which explains why that does not work.

[incorrect comments deleted]

Sorry, I didn’t realize that I have to actually spell it all out for you.

The consequence of the following sentences, which I quoted from the ATMega data sheet, i.e. “the documentation”

The 16-bit register must be byte accessed using two read or write operations.

Accessing the low byte triggers the 16-bit read or write operation.

is that in order to read TCNT1H, you have to first read TCNT1L. If you read the documentation further, you will understand that when you read the low byte TCNT1L, TCNT1H is copied into a temporary register, which is what is actually read instead of reading TCNT1H directly.

Your approach, which reads TNCT1H over and over again without reading TCNT1L, will always return the same value of the temporary register.

A while loop, expecting to see a change in TCNT1H, will be an endless loop.

OK. I apologize. I had somehow failed to take full notice of what you quoted from the datasheet. I had in fact read those words from the datasheet previously.

I had read the words differently. The way I read the documentation, TCNT1 as 16 bits must be done in the specified sequence TCNT1L, TCNT1H, which the compiler does when reading the 16-bit TCNT1. Yet my execution experience supports your interpretation. So you are right. My assessment was wrong.

I had presumed that that cached high byte would be read only once, and I would have hoped within a limited amount of time. If the TCNT1L always has to be read before you can get a fresh TCNTLH, 1. the documentation should have been much clearer on that. I guess I can now interpret the documents to say that whenever I read TCNT1H, I will get the cached upper byte of what ever 16-bit register was read last -- not necessarily the last value for TCNT1. 2. the compiler should be aware of that, and do a dummy TCNT1L read whenever you read TCNTLH.

This would seem to introduce the potential of some weird rare intermittent bugs when there is an interrupt that takes place after the TCNT1L (or similar) read and before the following TCNT1H read. This would seem to imply that, for safety, interrupts should be disabled during any access to 16-bit registers unless there is some special feature that keeps an interrupt from being serviced during the critical time.

This would seem to imply that, for safety, interrupts should be disabled during any access to 16-bit registers unless there is some special feature that keeps an interrupt from being serviced during the critical time.

There is such a feature. Within an interrupt, interrupts are blocked. So read the counter value into a 16 bit global variable while in the interrupt, and exit.

Alternatively, you can disable interrupts while the main program reads a 16 bit variable. But that doesn't work for timers and some other devices (like the ADC), because they keep running during the two 8-bit operations. It is for this reason that Atmel introduced the above discussed temporary register.

jremington: There is such a feature. Within an interrupt, interrupts are blocked. So read the counter value into a 16 bit global variable while in the interrupt, and exit.

I think some nest by re-enabling higher-priority interrupts in the ISR. I am only using the one ISR anyway.

The interrupt problem: Suppose both interrupt code and non-interrupt code were to read TCNT1 in C, or suppose one reads TCNT1 and the other reads UBRR1, UBRR0, OCR3B, OCR3A, ICR3, TCNT3, OCR1B, OCR1A, ICR1, or EEAR . (Not all of the processors have all of those registers, but if they do, the bug feature could supposedly occur.)

jremington: Alternatively, you can disable interrupts while the main program reads a 16 bit variable. But that doesn't work for timers and some other devices (like the ADC), because they keep running during the two 8-bit operations. It is for this reason that Atmel introduced the above discussed temporary register.

temporarily disabling interrupts does hold off the interrupts, which would allow the two reads to be effectively atomic.

My workaround is to get rid of all references to TCNT1H in my C code.

Is there a place in the Arduino Forum where they would actually like to hear about this, which I consider a tool set bug in implementing the architecture? Semantically it may be a library bug rather than a compiler bug, but somewhere in tool set, this is what I would consider a bug. One for-sure bug would be that any read access to TCNT1H must be immediately be preceded by an an access to TCNT1L. This would apply to any of the 16-bit registers. There is a similar consideration for writes to the low bytes. The not-a-bug portion would be to make the two accesses automatically atomic. Maybe generate a warning? Harder problem because the atomic actions hurts performance much more than an always-needed dummy read or dummy write.

The design of the ATMega family hardware is now about 20 years old, and its various intricacies have been discussed for about that long. The AVRFreaks forum is the place to go for the details.