Trying to understand interrupt sketch

EasyGoing1:
Thank you, but datasheets are typically not meant to be tutorials for helping people learn new things ... I'm looking for that golden nugget write up where someone who thoroughly understands the topic is able to explain it in easy to digest verbiage ... as opposed to something like a datasheet where the words tend to flow like a hail storm or a road littered with pot holes... words are hard to chew and they tend to upset the stomach ... :slight_smile:

1. The following diagram of Fig-1 hints that the physical Pin-4/PD2/DPin-2 of ATmega328P MCU can be configured to work as an 'interrupt line 0 (INT0 for interrupt type 0)' to receive interrupt signal from external hardware device K1. The diagram also hints that the physical Pin-5/PD3/DPin-3 can be configured to work as an 'interrupt line 1 (INT1 for interrupt type 1)' to receive interrupt signal from external hardware device K2. The diagram does not tell the procedures of how DPin-2 (DPin-3) can be attached with INT0 (INT1).
intZeroX.png
Figure-1: INT0 and INT1 interrupt structures of ATmega328P MCU

2. The procedures are (according to data sheets and practical results) listed below only for INT1 interrupt, and they will also be applicable for INT0 interrupt with corresponding change in the 'signal signatures'.

(1) DPin-3 will turn into an 'INT1 interrupt' line when the user puts HIGH at INT1-bit of EIMSK Register (Fig-2) and HIGH at I-bit of SREG Register (Fig-3). As a result, the occurrence of a triggering signal (LOW, Rising Edge, or Falling Edge) at DPin-3 will force the MCU (MCU will be interrupted) to suspend the current program that it has been executing and then jump to ISRINT1 (interrupt sub routine due to INT1 interrupt).


Figure-2: Bit layout of EIMSK Register

SREG.png
Figure-3: Bit layout of SREG Register

(a) Possible codes to store HIGH at INT1 bit of EIMSK Register

bitSet(EIMSK, 1);        //setBit(variable, bitPosition)
bitWrite(EIMSK, 1, HIGH);    //bitWrite(variable, bitPosition, bitValue)
EIMSK |= (1<<INT1);  //<< is not bit shift operation; bureaucratic style to store HIGH only at INT1 bit.
                         //| (pipe character) indicates OR operation and = indicates assignment

(b) Possible codes to store HIGH at I bit of SREG Register

bitSet(SREG, 7);
bitWrite(SREG, 7, HIGH);
sei();                      //set HIGH at global interrupt flag (I bit); opposite is: cli()
interrupts();             //Arduino Code; opposite is noInterrupts()
SREG |= (1<<I);               //not tested

(2) Setting the trigger level of the interrupting signal (the signal that will generate interrupt).
The Op has desired 'Rising Edge (RE)' as the trigger level of the interrupting signal. In Fig-1 when K2 is closed, a rising edge signal will appear at DPin-3. The RE trigger level is initialized by storing HIGH at both ISC11 (Interrupt Sense Control Bit 1 for INT1) and ISC10 bits of the EICRA Register (Fig-4).

isc11.png
Figure-4: Bit layout of EICRA Register

Possible codes to store HIGH at both ISC11 and ISC10 bits.
(a)

byte x = EICRA;
x = x | 0b00001100;
ECIRA = x;

(b)

bbitSet(EICRA, 3);  //HIGH ---> ISC11
bitSet(EICRA, 2);   //HIGH ---> ISC10

(c)

EICRA |= (1<<ISC11)|(1<<ISC10);    //untested

3. Final codes to initialize INT1 interrupt where DPin-3 is attached with INT1.

bitSet(EICRA, 3);   //HIGH ---> ISC11 bit
bitSet(EICRA, 2);          //HIGH ---> ISC10 bit

bitSet(EIMSK, 1);         //HIGH ---> INT1 bit
bitSet(SREG, 7);          //HIGH ---> I bit

After interruption, the MCU will vector at location 0x0004 which is redirected at the ISRINT1 routine by the following declaration:

ISR(INT1_vect)
{
   //insert codes as needed
}

All the above register level codes and ISR could be summarized by the declaration of the following Arduino codes:

attachInterrupt(digitalPinToInterrupt(3), ISRINT1, RISING);//DPin-3 for INT1, userISRName, triggerLevel

void ISRINT1()
{
   //insert codes as needed
}

4. The final hardware structure of INT1 interrupt logic


Figure-5: Hrardware structure of INT1 interrupt logic

OPeration: When rising edge occurs at DPin-3, the INTF1 flag assumes HIGH regardless of the open/close conditions of SWB and SWC (the interrupt enable/disable software switches). Once SWB and SWC are at closed conditions (usually done in the initialization process), the INTF1 flag will generate an interrupt request to the MCU. As a result, the MCU will first vector at location 0x0004 and then will jump to the ISR (interrupt sub routine) during which the INTF1 flag will be automatically cleared. This process is known as 'vectored interrupt'.

The status of INTF1 flag is also recorded at EIFR register so that the user can still detect the presence of a triggering signal at DPin-3 by polling the INTF1 flag in situation where interrupt structure remains disabled (SWB or SWC or both remain opened). This process is known as 'polled interrupt'; where, the INTF1 flag has to be cleared by the user by putting HIGH at the INTF1 bit of the EIFR register.

intZeroX.png

SREG.png

isc11.png

EasyGoing1:
EICRA = (1 << ISC11) | (1 << ISC10);

... is something else entirely and is not intuitively obvious to me that this line was changing a register in the CPU...

Incidentally, what is another way to write 1 << ISC11 ? Whats the pre-lambda way of saying the same thing?

Yea, it does take some digging. But, the beauty of the Open Source paradigm is that all the source code is available to you for inspection. If you work your way through the macro definitions, you'll eventually find that 'EICRA' is defined as:

(*(volatile uint8_t *)(0x69))

Then, looking up the address 0x69 in reference manual, you'll see:


So, that statement does indeed modify the contents of a register within the processor.

Also, as previously noted, the '<<' notation denotes a bit shift (to the left in this case). It's one of the most fundamental operations in the 'C' language and has absolutely nothing to do with Lambda Expressions.

Ok so wait … it’s making the ISC11th BIT a 1 and the rest zero … yet ISC11 is a bit label that has a function whos value will always be something between 0 and 7 …how can a BIT have a value higher than 1 ?

It can’t, but if you shift a 1 by three places to the left (1<<3) you end up with 1 in bit 3 with all the other bits zero.
This is exactly the same as writing b00001000 or 0x08 but those numbers are a little difficult to follow so the ISC11 variable is set to have a value of 3.

So all bit names are simply a variable set to the value of the bit number they refer to.

Therefore if you used (1<<ISC01) | (1<<ISC11) then you would get a number with a one in bit 3 and a one in bit 1 a value of b00001010 or 0x0A because the value of the variable ISC01 is one and the value of ISC11 is three.

Now as a quiz, what is the value of the variable ISC00 ?

The bit shifting is plain C language you can experiment with bit shifting & (and) | (or) on your home PC and a C compiler you don't need an arduino.

noweare:
The bit shifting is plain C language you can experiment with bit shifting & (and) | (or) on your home PC and a C compiler you don’t need an arduino.

And it is not just restricted to C, most languages, including Python, have these operations.

In practice I most often use bit shifting for joining two bytes together to make an int.

The AND operation is very useful for restricting a counting variable to a power of two. For example suppose you want to count over the range 0 to 7, that is eight values, then

count++;
count = count & 0x7;

Will do it.
Or if you want to decide if a number is odd or even then simply “look” at the least significant bit isolated with an AND operation.

if(number & 1) { // the number is odd