myProjects

Arduino Timer Interrupt ISR example

Datasheet Click Here for Microcontroller  ATmega328P manual (doc8161.pdf).
More about interrupt : http://it.wikipedia.org/wiki/Interrupt

Interrupts are used by microprocessor (CPU) and microcontroller (MCU) in order to inform process about availability of an event or information that a process is interested in handling (is a sort of asynchronous notify). An example could be  an interrupt which informs about pin status changing (for example from LOW (0v) to HIGH (5v) based on a threshlod light sensor).

ISR

On Arduino the name of routine which handles interrupts is pre-defined in library. The name of this routine is ISR (Interrupt Service Routine):

ISR(vector, attributes) { 
  <your_code_interrupt_handling> 
}

So adding ISR and implementing <your_code_interrupt_handling> we can  respond to an event (interrupt).
Note ISR is a macro defined in include file interrupt.h ( on-line source here )
Note More info here avr-libc

ISR Signature

The signature : #define ISR(Vector, Attributes)
Example       : ISR(TIMER2_OVF_vect, ISR_NOBLOCK) { /*your code*/ }

 Vector is the interrupts that you want to handle. This argument is vector of valid interrupts (valid for ATmega328P) such as TIMER2_OVF_vect (Timer/Counter2 Overflow interrupt).

Example :

ISR(TIMER2_OVF_vect)   { /*your code for OVF interrupt*/ } /* Timer/Counter2 Overflow */
ISR(TIMER2_COMPA_vect) { /*your code CPMPA match event*/ } /* Timer/Counter1 Compare Match A */

More ISR in your code you can have more than 1 ISR implementation (such as above two examples).

You can find the complete list of valid interrupts here :
  datasheet (here) of ATmega328P (pag. 65)
  iom328.h  (here and also at end of this post here)

(Click) Vectors List
Vector name      Vector definition
INT0_vect         /* External Interrupt Request 0 */
INT1_vect         /* External Interrupt Request 1 */
PCINT0_vect       /* Pin Change Interrupt Request 0 */
PCINT1_vect       /* Pin Change Interrupt Request 0 */
PCINT2_vect       /* Pin Change Interrupt Request 1 */
WDT_vect          /* Watchdog Time-out Interrupt */
TIMER2_COMPA_vect /* Timer/Counter2 Compare Match A */
TIMER2_COMPB_vect /* Timer/Counter2 Compare Match A */
TIMER2_OVF_vect   /* Timer/Counter2 Overflow */
TIMER1_CAPT_vect  /* Timer/Counter1 Capture Event */
TIMER1_COMPA_vect /* Timer/Counter1 Compare Match A */
TIMER1_COMPB_vect /* Timer/Counter1 Compare Match B */
TIMER1_OVF_vect   /* Timer/Counter1 Overflow */
TIMER0_COMPA_vect /* TimerCounter0 Compare Match A */
TIMER0_COMPB_vect /* TimerCounter0 Compare Match B */
TIMER0_OVF_vect   /* Timer/Couner0 Overflow */
SPI_STC_vect      /* SPI Serial Transfer Complete */
USART_RX_vect     /* USART Rx Complete */
USART_UDRE_vect   /* USART, Data Register Empty */
USART_TX_vect     /* USART Tx Complete */
ADC_vect          /* ADC Conversion Complete */
EE_READY_vect     /* EEPROM Ready */
ANALOG_COMP_vect  /* Analog Comparator */
TWI_vect          /* Two-wire Serial Interface */
SPM_READY_vect    /* Store Program Memory Read */

 Attributes: during the interrupt handling (while the routine ISR is executing  <your_code_interrupt_handling>) there isn't any new invocation of ISR (this is default behaviour ISR_BLOCK). So you have no new interrupts while your code is executing this in order to avoid cycles.
You can change this behaviour using  ISR_BLOCK, ISR_NOBLOCK, ISR_NAKED and ISR_ALIASOF(vect).

Example :

ISR(TIMER2_OVF_vect, ISR_NOBLOCK)   { /*your code for OVF interrupt*/ } /* Timer/Counter2 Overflow */
 /*if routine ISR is executing then all new interrupts are blocked*/
 #define ISR_BLOCK
 /*if routine ISR is executing all new interrupts are not blocked*/
 #define ISR_NOBLOCK
 /*are not generated support code for start and end of interrupt handling : developer has to handle that */
 #define ISR_NAKED
 /* ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)) : PCINT1_vect and PCINT0_vect share the code*/
 #define ISR_ALIASOF(target_vector)

Example Arduino timer (TIMER2_OVF)

In below example we are going to use Timer and  related interrupts.
There are 3 Timers :
Two 8-bit Timer/Counters with Separate Prescaler and Compare Mode
One 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and Capture Mode
We are going to use TIMER/Counter2 and TIMER2_OVF interrupt.

timer2_ovf
timer interrupts (timer2_ovf)

TIMER/Counter2 is a 8 bit and the associated counter TCNT2 is increased +1 each clock tick. This means that each 255(0xFF) clock ticks the related counter (TCNT2) value is reset to zero (counter is overflowed) and TIMER2_OVF(TIMER2_OVF_vect) interrupt is fired.

Frequency of  TIMER2_OVF event

Assuming that the clock frequency is Freq=16Mhz (later we'll see that we can use scale to reduce Freq) the period is 62.5ns. About TCNT2 overflow we need 255 ticks in order to have overflow interrupt (TIMER2_OVF).

Frequency overflow Interrupt
Frequency overflow Interrupt

So overflow interrupt occurs with:
Freq_OVF = 62.5ns*255 = 16MHz/255 = 62.5Khz

Above is assuming TCNT2 from 0(0x00) to 255(0xFF) but the range can be easily changed as showed in below example. In fact if we force TCNT2 to start, after overflow interrupt, from TCNT2init=121 (for example) we need 255-121 ticks in order to reach 255 (then trigger overflow interrupt).

In this case overflow interrupt occurs with:
Freq_OVF_121 = 62.5ns*(255-121) = 16MHz/(255-121) = 119.kKhz

Theoretically  ISR(TIMER2_OVF_vect) { <you_code> }
<you_code> has a cost in terms of cycles, in fact your code could requires ms in order to be executed. This mean that you can't reach any frequency you want: attempting to measure this I found ~100Khz as limit.

Prescaler

TIMER/Counter2 can be clocked internally using Prescaler.
Prescaler is used in order to reduce internal system clock frequency dividing system clock signal (which is at 16MHz on my version) by a constant number :
  1, 8, 32, 64, 128, 256, or 1024

Prescaler can be selected with the proper bit (CS22,CS21 and CS20) 0f TCCR2B.

 TCCR2B |= (1<<CS22)|(1<<CS20); // Prescale 128 (Timer/Counter started)
 TCCR2B &= ~(1<<CS21);          // CS22=1 CS21=0 CS20=1 -> prescale = 128
Arduino Timer Prescale
Arduino Timer Prescale

So, for example, if Freq=16Mhz and we set Prescaler to 32 the resulting freq is:
Freq_s32 = 16Mhz/32 = 500Khz

In this case overflow interrupt prescaled occurs with:
Freq_OVF = 2us*255 = 500KHz/255 = 1.9Khz

 Final calculation for Freq_OVF is : Freq_OVF = Freq / scale*(255 - TCNT2init) 

frequency_pin
Frequency PIN

 If you use TIMER2_OVF in order to toggle a PIN (above image) the frequency is :
Freq_PIN = Freq_OVF / 2
..or..
Freq_PIN = Freq / 2*scale*(255 - TCNT2init)= 16000000 / 2*scale*(255 - TCNT2init)

frequency

Example code

About Port manipulation a useful link is :
https://www.arduino.cc/en/Reference/PortManipulation

... and related Port Mapping :
https://www.arduino.cc/en/Hacking/Atmega168Hardware

Arduino ports mapping
Arduino ports mapping

Based on above notes:

  •  simple pin mode selector is
      DDRD = DDRD | B00000100; //pinMode(2, OUTPUT);
  • simple pin toggle status code is
     PORTD ^= (1<<2); //digitalWrite(2, !digitalRead(2));

Below image is probe on PIN 2 as result of code here.
TCNT2Init = 130
Scale = 128
Freq_PIN = 16000000 / 2*128*(255 - 130)= 500Hz
Period_PIN = 2ms
Time per division (below image) is 1ms

Arduino PIN oscilloscope emulator (Scale=128 , TCNT2init=130)
Arduino PIN oscilloscope emulator (Scale=128 , TCNT2init=130)
char TCNT2init= 130;

void setup() {
 //pinMode(2, OUTPUT);
 //https://www.arduino.cc/en/Reference/PortManipulation
 // DDRD - The Port D Data Direction Register - read/write
 // D (digital pins 0 to 7)
 DDRD = DDRD | B00000100; 
 
 Serial.begin(9600);
 
 Serial.println("-----BEFORE------");
 Serial.print("TCCR2A = ");
 Serial.println(TCNT2,BIN);
 Serial.print("TCCR2B = ");
 Serial.println(TCCR2B,BIN);
 Serial.print("TIMSK2 = ");
 Serial.println(TIMSK2,BIN);
 
 // TIMER2_OVF_vect (Timer2 overflow) is fired with freq: 
 // Freq_OVF = 16000000/(scale*(255-TCNT2init)) Hz
 // Square wave( _-_-_ ) on pin OVF_Pin has:
 // Freq_PIN = FreqOVF/2 

 TCCR2B = 0x00; // No clock source (Timer/Counter stopped) 
 
 TCNT2 = TCNT2init; // Register : the Timer/Counter (TCNT2) and Output Compare Register (OCR2A and OCR2B) are 8-bit
                    // Reset Timer Count
 
 TCCR2A = 0x00; // TCCR2A – Timer/Counter Control Register A
                // All bits to zero -> Normal operation
 
 TCCR2B |= (1<<CS22)|(1<<CS20); // Prescale 128 (Timer/Counter started)
 TCCR2B &= ~(1<<CS21);          // CS22=1 CS21=0 CS20=1 -> prescale = 128
 
 TIMSK2 |= (1<<TOIE2); // TIMSK2 – Timer/Counter2 Interrupt Mask Register
 // Bit 0 – TOIE2: Timer/Counter2 Overflow Interrupt Enable
 
 Serial.println("----- AFTER ------");
 Serial.print("TCCR2A = ");
 Serial.println(TCNT2,BIN);
 Serial.print("TCCR2B = ");
 Serial.println(TCCR2B,BIN);
 Serial.print("TIMSK2 = ");
 Serial.println(TIMSK2,BIN);
}

void loop(){
 //codes are in ISR(TIMER2_OVF_vect)
}

ISR(TIMER2_OVF_vect) { 
 //https://www.arduino.cc/en/Hacking/Atmega168Hardware
 // PORTD maps to Arduino digital pins 0 to 7
 TCNT2 = TCNT2init;
 PORTD ^= (1<<2); 
};

Below more images with different value of Presale and TCNT2init

Valid Interrupt Vectors


 1: /* Interrupt Vectors */
 2: /* Interrupt Vector 0 is the reset vector. */
 3: #define INT0_vect _VECTOR(1)          /* External Interrupt Request 0 */
 4: #define INT1_vect _VECTOR(2)          /* External Interrupt Request 1 */
 5: #define PCINT0_vect _VECTOR(3)        /* Pin Change Interrupt Request 0 */
 6: #define PCINT1_vect _VECTOR(4)        /* Pin Change Interrupt Request 0 */
 7: #define PCINT2_vect _VECTOR(5)        /* Pin Change Interrupt Request 1 */
 8: #define WDT_vect _VECTOR(6)           /* Watchdog Time-out Interrupt */
 9: #define TIMER2_COMPA_vect _VECTOR(7)  /* Timer/Counter2 Compare Match A */
 10: #define TIMER2_COMPB_vect _VECTOR(8) /* Timer/Counter2 Compare Match A */
 11: #define TIMER2_OVF_vect _VECTOR(9)   /* Timer/Counter2 Overflow */
 12: #define TIMER1_CAPT_vect _VECTOR(10) /* Timer/Counter1 Capture Event */
 13: #define TIMER1_COMPA_vect _VECTOR(11)/* Timer/Counter1 Compare Match A */
 14: #define TIMER1_COMPB_vect _VECTOR(12)/* Timer/Counter1 Compare Match B */
 15: #define TIMER1_OVF_vect _VECTOR(13)   /* Timer/Counter1 Overflow */
 16: #define TIMER0_COMPA_vect _VECTOR(14) /* TimerCounter0 Compare Match A */
 17: #define TIMER0_COMPB_vect _VECTOR(15) /* TimerCounter0 Compare Match B */
 18: #define TIMER0_OVF_vect _VECTOR(16)   /* Timer/Couner0 Overflow */
 19: #define SPI_STC_vect _VECTOR(17)      /* SPI Serial Transfer Complete */
 20: #define USART_RX_vect _VECTOR(18)     /* USART Rx Complete */
 21: #define USART_UDRE_vect _VECTOR(19)   /* USART, Data Register Empty */
 22: #define USART_TX_vect _VECTOR(20)     /* USART Tx Complete */
 23: #define ADC_vect _VECTOR(21)          /* ADC Conversion Complete */
 24: #define EE_READY_vect _VECTOR(22)     /* EEPROM Ready */
 25: #define ANALOG_COMP_vect _VECTOR(23)  /* Analog Comparator */
 26: #define TWI_vect _VECTOR(24)          /* Two-wire Serial Interface */
 27: #define SPM_READY_vect _VECTOR(25)    /* Store Program Memory Read */
Powered by WP Review

About the author

busycrack

2 Comments

Click here to post a comment

Posts