Timer Interrupts

Hi guys!
I am trying to learn how to control the timer registers of an Arduino Uno because i want more flexibility about it than some libraries can offer.

TeachMeMicro - Timer interrupt tutorial
In this site i got some pretty good and easy info about it. I understand some of the theory, but i'm in doubt about setting the registers.

TIMSK2 = (TIMSK2 & B11111110) | 0x01 //use mask so that only the least significant bit is affected
TCCR2B = (TCCR2B & B11111000) | 0x07 //use mask so that only the least three significant bits are affected

If i get it right: They use a registers to set the interrupts, a register to manage the timer divisor and make the rotine that's executed in certain times. But about these two lines that set the registers:

  • The operation "(TIMSK2 & B11111110)" is setting the bits of register, but what is "TIMSK2 &" doing there?
  • "| 0x01" would be the mask to protect the other bits of the register? So when i write the register, doesn't matter what i write in the five most significant bits?
  • Can i enable the timer overflow and timer compare interrupts together?
  • If i understand well, to get the two types of interrupts and a divisor of 64 i would write this code?

TIMSK2 = (TIMSK2 & B11111111) | 0x07;
TCCR2B = (TCCR2B & B11111100) | 0x04;
OCR2A = 100;
OCR2B = 200;

My idea of using the timer overflow and timer compare interrupts together is because i was thinking in making a simple clock with the overflow timer and letting the compare ones to other timed events, like checking sensors, data transfer between devices or anything useful.

DiegoT96:
The operation "(TIMSK2 & B11111110)" is setting the bits of register, but what is "TIMSK2 &" doing there?

It's clearing bit zero.

DiegoT96:
"| 0x01" would be the mask to protect the other bits of the register?

No, that's to set bit zero to a 1.

DiegoT96:
So when i write the register, doesn't matter what i write in the five most significant bits?

It does matter. If one of the bits 3-7 was 'preserved' as a zero by the previous AND'ing and you mistakenly OR in a 1 to that bit location you've corrupted your data.

DiegoT96:
Can i enable the timer overflow and timer compare interrupts together?

Yes, they each have their own interrupt vectors.

DiegoT96:
If i understand well, to get the two types of interrupts and a divisor of 64 i would write this code?

I didn't look closely but, you can peruse the datasheet and experiment.

It seems like you might want to brush up on boolean algebra.

dougp:
It seems like you might want to brush up on boolean algebra.

And Bitwise Operations.

(TIMSK2&B11111110) returns the bitwise and of the current value of TIMSK2 and B11111110, that is, all bits except the lowest bit. Then it is bitwise or'ed ( | ) with 0x01, ie, 1.

Strictly speaking, TIMSK2&B11111110 is not needed, but it is good practice because it makes the intent clear - TIMSK2=TIMSK2|0x01 would do the same thing, since regardless of what the low bit was, when you bitwise-or it with 0x01, it's going to be a 1. You can further shorten that to TIMSK2|=0x01 (|= is like the more common += operator).

(TCCR2B & B11111000) | 0x07

is similarly more verbose than it needs to be.

But this is only the case because in both of those examples, the only bits they are changing are being set to 1; if for example you wanted to set the three low bits to 0x06 instead of 0x07, you would need the (TCCR2B&B11111000), because you need the lowest bit to be 0, and that wouldn't be guaranteed otherwise.

So using the mask like that is good practice.

To enable all three interrupts, the analogous line would be:

TIMSK2 = (TIMSK2 & B11111000) | 0x07

Now if you look at the datasheet, you'll see that only the low three bits of TIMSK2 do anything. So you can just do:
TIMSK2 = 0x07.

For prescaler of 64:
TCCR2B = (TCCR2B & B11111000) | 0x04; //what you wrong will work too (as noted above), but is poor practice - if you're using a mask, which you need to here, mask off all the bits that aren't relevant.

In your description above - the registers enable the interrupt routines. You still need to make the interrupt routines with the ISR() macro. Be sure that you always define any ISRs that you enable, otherwise the chip will end up in a bad state (iirc, it will end up at the start of the program, but still thinking it's in the ISR and with interrupts disabled for that reason).

My idea of using the timer overflow and timer compare interrupts together is because i was thinking in making a simple clock with the overflow timer and letting the compare ones to other timed events, like checking sensors, data transfer between devices or anything useful.

Forget it mills() and micros are the best way to do those sort of things not the timers.

Mark

holmes4:
Forget it mills() and micros are the best way to do those sort of things not the timers.

Mark

Somehow I missed that part - yeah, using interrupts for that is a very bad idea. Just use millis() (and micros() if appropriate) for that sort of task.

You generally want as little happening in an ISR as possible - only the bare minimum that needs to happen at that exact instant. Often you just set a flag that your main loop will read and deal with the next time through the loop, or record the micros() when something happened... that kind of thing. No serial printing, no interaction with external devices, no time delays of any sort. Depending on what kind of sensor you're interacting with, it may not work at all because it might depend on interrupts... which are disabled inside the ISR.

Maybe ultimate efficiency isn't the main goal here. It could be that a part of @DiegoT96's motive is just simple curiosity about what's going on down there in the innards of the chip. If nothing else, one can learn how much actually goes on behind the scenes with things like analogWrite(), for example.

@OP

TIMSK2 = (TIMSK2 & B11111110) | 0x01 //use mask so that only the least significant bit is affected

Let us make an alternative way of understanding the need and meaning of the above operation with the help of the following diagram which depicts the bit pattern of the TIMSK2 Register.


Figure-1: Bit pattern and explanation of TIMSK2 Register

1. A Brief Background
In 'Normal Mode' operation, the TC2 Module of ATmega328P keeps counting the incoming pulses as 'up counter'. The count may begin from 0x00 or from a preset-count. When the TC2 arrives at the full count (0xFF) and then receives one more pulse, the content of TC2 rolls over from all 1s to all 0s -- an event known as overflow which when occurs, the TOV2 (TC2 Overflow Flag) flag assumes LH-state. A user program can detect the active state of the TOV2 flag in two ways: (1) by polling process where the user program continuously monitors the value of the flag bit and (2) interrupt process where the TOV2 flag itself interrupts the user program/MCU when the flag becomes active. (TOV2 flag is the part of TIFR2 Register.)

The interrupt process will work (will be enabled) if Bit-0 of the TIMSK2 Register of Fig-1 is set to LH- value during initialization. (The I-Bit of the SREG Register should also be set to LH-state.)

2. Codes to Place Logic High (LH) at Bit-0 of TIMSK2 Register
(1) Read the present content of TIMSK2 Register:

byte x = TIMSK2; //( x= . . . . . . . .)

(2) Place Logic Low (0) at Bit-0 position only without affecting the values of other bits:

x = x&0b11111110; //(x = . . . . . . . 0)

(3) Place Logic High (1) at Bit-0 position:

x = x | 0b000000001;  //(x = . . . . . . . 1)

3. All the separate 3 codes of Step-2 can be written by the following 1-line code:

TIMSK2 = (TIMSK2&0b11111110)|0b00000001;
==> TIMSK2 = (TIMSK2&0b11111110)|0x01;

4. The objective (putting LH at Bit-0 of TIMSK2 Register) of Step-3 can be achieved by executing the following single line Arduino code:

bitSet(TIMSK2, 0);                     //Edit: register,  bit-position
==> bitWrite(TIMSK2, 0, 1);  //Edit: register, bit-position, bit-value

dougp:
I didn't look closely but, you can peruse the datasheet and experiment.

It seems like you might want to brush up on boolean algebra.

gfvalvo:
And Bitwise Operations

After your answer i give a better look in datasheet, i undestood what i want to change in register, maybe a closer look to the algebra will be great because i really don't get right how to operate it.

DrAzzy:
To enable all three interrupts, the analogous line would be:

TIMSK2 = (TIMSK2 & B11111000) | 0x07

Now if you look at the datasheet, you'll see that only the low three bits of TIMSK2 do anything. So you can just do:
TIMSK2 = 0x07.

For prescaler of 64:
TCCR2B = (TCCR2B & B11111000) | 0x04; //what you wrong will work too (as noted above), but is poor practice - if you're using a mask, which you need to here, mask off all the bits that aren't relevant.

Well, in TIMSK2 i could use 0x07 (or other) because the bits 7-3 doesn't do anything. But in TCCR2B there is other bits that matter to program, but i just wanna change the 3 first bits...so, how exactly do i mask the bits that shouldn't be affected? I don't get what part of the code are masking off the other bits

holmes4:
Forget it mills() and micros are the best way to do those sort of things not the timers.

This would be a lot easier to make a 24h clock, but i was thinking in making a compilation of projects in the same board (like a clock, controller for games, controlling diverses devices in the room and testing prototypes...i want it to be a menu acessing diverses functions) so it could not check millis() ou micros() by days. And the possibility of using the same interruption to know the time to capture data from other devices would be great instead of using some delays and millis() logics. I didn't make clear my ideia of using the timer interruptions, but it's more learning proposes than making a really useful project.

dougp:
Maybe ultimate efficiency isn't the main goal here. It could be that a part of @DiegoT96's motive is just simple curiosity about what's going on down there in the innards of the chip. If nothing else, one can learn how much actually goes on behind the scenes with things like analogWrite(), for example.

You say everything hehehe i like to learn new things and understand what i'm doing. Getting a ready-to-go code is easy, there is so much examples on web, but learning to operate the register is better to understand. It's good to really know what i'm doing, it make thing easier, better to understand other concepts.

GolamMostafa:
Let us make an alternative way of understanding the need and meaning of the above operation with the help of the following diagram which depicts the bit pattern of the TIMSK2 Register.


Figure-1: Bit pattern and explanation of TIMSK2 Register

1. A Brief Background
In 'Normal Mode' operation, the TC2 Module of ATmega328P keeps counting the incoming pulses as 'up counter'. The count may begin from 0x00 or from a preset-count. When the TC2 arrives at the full count (0xFF) and then receives one more pulse, the content of TC2 rolls over from all 1s to all 0s -- an event known as overflow which when occurs, the TOV2 (TC2 Overflow Flag) flag assumes LH-state. A user program can detect the active state of the TOV2 flag in two ways: (1) by polling process where the user program continuously monitors the value of the flag bit and (2) interrupt process where the TOV2 flag itself interrupts the user program/MCU when the flag becomes active. (TOV2 flag is the part of TIFR2 Register.)

The interrupt process will work (will be enabled) if Bit-0 of the TIMSK2 Register of Fig-1 is set to LH- value during initialization. (The I-Bit of the SREG Register should also be set to LH-state.)

2. Codes to Place Logic High (LH) at Bit-0 of TIMSK2 Register
(1) Read the present content of TIMSK2 Register:

byte x = TIMSK2; //( x= . . . . . . . .)

(2) Place Logic Low (0) at Bit-0 position only without affecting the values of other bits:

x = x&0b11111110; //(x = . . . . . . . 0)

(3) Place Logic High (1) at Bit-0 position:

x = x | 0b000000001;  //(x = . . . . . . . 1)

3. All the separate 3 codes of Step-2 can be written by the following 1-line code:

TIMSK2 = (TIMSK2&0b11111110)|0b00000001;

==> TIMSK2 = (TIMSK2&0b11111110)|0x01;




**4.** The objective (putting LH at Bit-0 of TIMSK2 Register) of Step-3 can be achieved by executing the following single line Arduino code:


bitSet(TIMSK2, 0);
==> bitWrite(TIMSK2, 0, 1);

Golam, great explanation, i was looking better at the datasheet to understand what's going on before trying to operate the registers of MCU, but you make it seems so easy hahaha
Well, so i get what's going on in MCU, what the registers control or feedback, what generate the interruption and what i have to write or read in the registers (so i can read TIFR2 flags instead of using interruptions), i get which bits write in which registers...but how to write these bits?

Reading a registers seems a little easier than write, if i'm right it can be done by taking the register bit to a variable with bitRead() or taking all register value to a variable, and if thing get messed up it would be just with the variable and the program result going wrong.
But messing at writing register is a little more worrier. The bitWrite() operations looks preety simple and errorless way to change the bits values, it's a good practice to use this method?
And about the "TIMSK2 = (TIMSK2&0b11111110)|0b00000001;" way. To change both TIMSK2 and TCCR2B it's the same way, right?
So working with this command line by parts would be:

  • "TIMSK2=" setting the bits value of the register to:
  • "(TIMSK2&0b11111xxx)" to set values to low level and remain the rest at current value with AND bitwise or "(TIMSK2|0b00000xxx)" in the case of changing to high level
  • "|0b00000001;" to add the OR bitwise to te same commandline

Thank you all guys, great community you have here :smiley: