Wednesday, June 27, 2012

AVR Tutorial - 4. Interrupts




In a microcontroller or in any microprocessor, we could check the status of a device or input from a pin in two different ways:
i.                    Polling
ii.                  Interrupt Method
In polling the µC periodically check for the input or status flags from the external peripheral to get the status of its working. In the case of interrupt method when the external (or even internal) peripheral needs the attention of the processor, it will trigger an interrupt and the processor will stop execution of the current statements and store the present program address in system stack, execute a set of codes called ISR (Interrupt Service Routine) associated with the particular interrupt and restores the lastly executed program from stack and continue its work.

            In this chapter we will be demonstrating how we can make an external interrupt to trigger our ATMEGA32 µC. We will be connecting an external switch to the external interrupt 0 (INT0) of our µC and toggling the led connected to the circuit.

            In order to trigger enable the external interrupt we must do three things mainly:
i.                    Unmask the needed external interrupt in the GICR (General Interrupt Control Register).


Unmask the INT0 bit for enabling INT0
Unmask the INT1 bit for enabling INT1
Unmask the INT2 bit for enabling INT2


ii.                  Set the interrupt sense bits in the MCUCR (MCU Control Register), ie. to enable interrupt when there is a change in voltage level / when there is a rising edge / falling edge etc.

iii.                Set the global interrupt enable bit in the SREG which is being done by calling the function sei( );
Example 1: Toggling LED on External Interrupt INT0



Program:
#include <avr/io.h>
#include <util/delay.h>
#include<avr/interrupt.h>


void main()
{
            DDRB = 0b00000001;
            PORTB = 0b00000001;
           
            GICR = 0b01000000; //Enabling external interrupt 0
            MCUCR = 0b00000011; //INT0 triggered on rising edge
            sei( );              
            while(1)
            {
            }
}

ISR(INT0_vect) //Interrupt Service Routine for external Interrupt
{

            PORTB ^= 0b00000001;

}




·         Here we have included the interrupt.h header file which do contains the vector number and other interrupt related definitions for using external interrupts.
·         In the circuit we have connected a switch to the INT0th pin with a 10K pull up resistor so that is we press switch it will become logical low and else it will be logical high.
·         In GICR we have enabled just the external interrupt 0 ie. INT0.
·         We have set the MCUCR to trigger the INT0 on external interrupt 0 on rising edge.
·         We have called the sei( ) interrupt enabler function.
·         We have used ISR(INT0_vect) to write the statements associated with the INT0 interrupt. INT0_vect is the alias for interrupt vector number whose definition comes in the interrupt.h header file.

The following tables give the list of all available interrupts and its use:
Vector name
Old vector name
Description
ADC_vect
SIG_ADC
ADC Conversion Complete
ANALOG_COMP_0_vect
SIG_COMPARATOR0
Analog Comparator 0
ANALOG_COMP_1_vect
SIG_COMPARATOR1
Analog Comparator 1
ANALOG_COMP_2_vect
SIG_COMPARATOR2
Analog Comparator 2
ANALOG_COMP_vect
SIG_COMPARATOR
Analog Comparator
ANA_COMP_vect
SIG_COMPARATOR
Analog Comparator
CANIT_vect
SIG_CAN_INTERRUPT1
CAN Transfer Complete or Error
EEPROM_READY_vect
SIG_EEPROM_READY, SIG_EE_READY
EE_RDY_vect
SIG_EEPROM_READY
EEPROM Ready
EE_READY_vect
SIG_EEPROM_READY
EEPROM Ready
EXT_INT0_vect
SIG_INTERRUPT0
External Interrupt Request 0
INT0_vect
SIG_INTERRUPT0
External Interrupt 0
INT1_vect
SIG_INTERRUPT1
External Interrupt Request 1
INT2_vect
SIG_INTERRUPT2
External Interrupt Request 2
INT3_vect
SIG_INTERRUPT3
External Interrupt Request 3
INT4_vect
SIG_INTERRUPT4
External Interrupt Request 4
INT5_vect
SIG_INTERRUPT5
External Interrupt Request 5
INT6_vect
SIG_INTERRUPT6
External Interrupt Request 6
INT7_vect
SIG_INTERRUPT7
External Interrupt Request 7
IO_PINS_vect
SIG_PIN, SIG_PIN_CHANGE
External Interrupt Request 0
LCD_vect
SIG_LCD
LCD Start of Frame
LOWLEVEL_IO_PINS_vect
SIG_PIN
Low-level Input on Port B
OVRIT_vect
SIG_CAN_OVERFLOW1
CAN Timer Overrun
PCINT0_vect
SIG_PIN_CHANGE0
Pin Change Interrupt Request 0
PCINT1_vect
SIG_PIN_CHANGE1
Pin Change Interrupt Request 1
PCINT2_vect
SIG_PIN_CHANGE2
Pin Change Interrupt Request 2
PCINT3_vect
SIG_PIN_CHANGE3
Pin Change Interrupt Request 3
PCINT_vect
SIG_PIN_CHANGE, SIG_PCINT
PSC0_CAPT_vect
SIG_PSC0_CAPTURE
PSC0 Capture Event
PSC0_EC_vect
SIG_PSC0_END_CYCLE
PSC0 End Cycle
PSC1_CAPT_vect
SIG_PSC1_CAPTURE
PSC1 Capture Event
PSC1_EC_vect
SIG_PSC1_END_CYCLE
PSC1 End Cycle
PSC2_CAPT_vect
SIG_PSC2_CAPTURE
PSC2 Capture Event
PSC2_EC_vect
SIG_PSC2_END_CYCLE
PSC2 End Cycle
SPI_STC_vect
SIG_SPI
Serial Transfer Complete
SPM_RDY_vect
SIG_SPM_READY
Store Program Memory Ready
SPM_READY_vect
SIG_SPM_READY
Store Program Memory Read
TIM0_COMPA_vect
SIG_OUTPUT_COMPARE0A
Timer/Counter Compare Match A
TIM0_COMPB_vect
SIG_OUTPUT_COMPARE0B
Timer/Counter Compare Match B
TIM0_OVF_vect
SIG_OVERFLOW0
Timer/Counter0 Overflow
TIM1_CAPT_vect
SIG_INPUT_CAPTURE1
Timer/Counter1 Capture Event
TIM1_COMPA_vect
SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match A
TIM1_COMPB_vect
SIG_OUTPUT_COMPARE1B
Timer/Counter1 Compare Match B
TIM1_OVF_vect
SIG_OVERFLOW1
Timer/Counter1 Overflow
TIMER0_CAPT_vect
SIG_INPUT_CAPTURE0
ADC Conversion Complete
TIMER0_COMPA_vect
SIG_OUTPUT_COMPARE0A
TimerCounter0 Compare Match A
TIMER0_COMPB_vect
SIG_OUTPUT_COMPARE0B, SIG_OUTPUT_COMPARE0_B
Timer Counter 0 Compare Match B
TIMER0_COMP_A_vect
SIG_OUTPUT_COMPARE0A, SIG_OUTPUT_COMPARE0_A
Timer/Counter0 Compare Match A
TIMER0_COMP_vect
SIG_OUTPUT_COMPARE0
Timer/Counter0 Compare Match
TIMER0_OVF0_vect
SIG_OVERFLOW0
Timer/Counter0 Overflow
TIMER0_OVF_vect
SIG_OVERFLOW0
Timer/Counter0 Overflow
TIMER1_CAPT1_vect
SIG_INPUT_CAPTURE1
Timer/Counter1 Capture Event
TIMER1_CAPT_vect
SIG_INPUT_CAPTURE1
Timer/Counter Capture Event
TIMER1_CMPA_vect
SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match 1A
TIMER1_CMPB_vect
SIG_OUTPUT_COMPARE1B
Timer/Counter1 Compare Match 1B
TIMER1_COMP1_vect
SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match
TIMER1_COMPA_vect
SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match A
TIMER1_COMPB_vect
SIG_OUTPUT_COMPARE1B
Timer/Counter1 Compare MatchB
TIMER1_COMPC_vect
SIG_OUTPUT_COMPARE1C
Timer/Counter1 Compare Match C
TIMER1_COMPD_vect
SIG_OUTPUT_COMPARE0D
Timer/Counter1 Compare Match D
TIMER1_COMP_vect
SIG_OUTPUT_COMPARE1A
Timer/Counter1 Compare Match
TIMER1_OVF1_vect
SIG_OVERFLOW1
Timer/Counter1 Overflow
TIMER1_OVF_vect
SIG_OVERFLOW1
Timer/Counter1 Overflow
TIMER2_COMPA_vect
SIG_OUTPUT_COMPARE2A
Timer/Counter2 Compare Match A
TIMER2_COMPB_vect
SIG_OUTPUT_COMPARE2B
Timer/Counter2 Compare Match A
TIMER2_COMP_vect
SIG_OUTPUT_COMPARE2
Timer/Counter2 Compare Match
TIMER2_OVF_vect
SIG_OVERFLOW2
Timer/Counter2 Overflow
TIMER3_CAPT_vect
SIG_INPUT_CAPTURE3
Timer/Counter3 Capture Event
TIMER3_COMPA_vect
SIG_OUTPUT_COMPARE3A
Timer/Counter3 Compare Match A
TIMER3_COMPB_vect
SIG_OUTPUT_COMPARE3B
Timer/Counter3 Compare Match B
TIMER3_COMPC_vect
SIG_OUTPUT_COMPARE3C
Timer/Counter3 Compare Match C
TIMER3_OVF_vect
SIG_OVERFLOW3
Timer/Counter3 Overflow
TIMER4_CAPT_vect
SIG_INPUT_CAPTURE4
Timer/Counter4 Capture Event
TIMER4_COMPA_vect
SIG_OUTPUT_COMPARE4A
Timer/Counter4 Compare Match A
TIMER4_COMPB_vect
SIG_OUTPUT_COMPARE4B
Timer/Counter4 Compare Match B
TIMER4_COMPC_vect
SIG_OUTPUT_COMPARE4C
Timer/Counter4 Compare Match C
TIMER4_OVF_vect
SIG_OVERFLOW4
Timer/Counter4 Overflow
TIMER5_CAPT_vect
SIG_INPUT_CAPTURE5
Timer/Counter5 Capture Event
TIMER5_COMPA_vect
SIG_OUTPUT_COMPARE5A
Timer/Counter5 Compare Match A
TIMER5_COMPB_vect
SIG_OUTPUT_COMPARE5B
Timer/Counter5 Compare Match B
TIMER5_COMPC_vect
SIG_OUTPUT_COMPARE5C
Timer/Counter5 Compare Match C
TIMER5_OVF_vect
SIG_OVERFLOW5
Timer/Counter5 Overflow
TWI_vect
SIG_2WIRE_SERIAL
2-wire Serial Interface
TXDONE_vect
SIG_TXDONE
Transmission Done, Bit Timer Flag 2 Interrupt
TXEMPTY_vect
SIG_TXBE
Transmit Buffer Empty, Bit Itmer Flag 0 Interrupt
UART0_RX_vect
SIG_UART0_RECV
UART0, Rx Complete
UART0_TX_vect
SIG_UART0_TRANS
UART0, Tx Complete
UART0_UDRE_vect
SIG_UART0_DATA
UART0 Data Register Empty
UART1_RX_vect
SIG_UART1_RECV
UART1, Rx Complete
UART1_TX_vect
SIG_UART1_TRANS
UART1, Tx Complete
UART1_UDRE_vect
SIG_UART1_DATA
UART1 Data Register Empty
UART_RX_vect
SIG_UART_RECV
UART, Rx Complete
UART_TX_vect
SIG_UART_TRANS
UART, Tx Complete
UART_UDRE_vect
SIG_UART_DATA
UART Data Register Empty
USART0_RXC_vect
SIG_USART0_RECV
USART0, Rx Complete
USART0_RX_vect
SIG_UART0_RECV
USART0, Rx Complete
USART0_TXC_vect
SIG_USART0_TRANS
USART0, Tx Complete
USART0_TX_vect
SIG_UART0_TRANS
USART0, Tx Complete
USART0_UDRE_vect
SIG_UART0_DATA
USART0 Data Register Empty
USART1_RXC_vect
SIG_USART1_RECV
USART1, Rx Complete
USART1_RX_vect
SIG_UART1_RECV
USART1, Rx Complete
USART1_TXC_vect
SIG_USART1_TRANS
USART1, Tx Complete
USART1_TX_vect
SIG_UART1_TRANS
USART1, Tx Complete
USART1_UDRE_vect
SIG_UART1_DATA
USART1, Data Register Empty
USART2_RX_vect
SIG_USART2_RECV
USART2, Rx Complete
USART2_TX_vect
SIG_USART2_TRANS
USART2, Tx Complete
USART2_UDRE_vect
SIG_USART2_DATA
USART2 Data register Empty
USART3_RX_vect
SIG_USART3_RECV
USART3, Rx Complete
USART3_TX_vect
SIG_USART3_TRANS
USART3, Tx Complete
USART3_UDRE_vect
SIG_USART3_DATA
USART3 Data register Empty
USART_RXC_vect
SIG_USART_RECV, SIG_UART_RECV
USART, Rx Complete
USART_RX_vect
SIG_USART_RECV, SIG_UART_RECV
USART, Rx Complete
USART_TXC_vect
SIG_USART_TRANS, SIG_UART_TRANS
USART, Tx Complete
USART_TX_vect
SIG_USART_TRANS, SIG_UART_TRANS
USART, Tx Complete
USART_UDRE_vect
SIG_USART_DATA, SIG_UART_DATA
USART Data Register Empty
USI_OVERFLOW_vect
SIG_USI_OVERFLOW
USI Overflow
USI_OVF_vect
SIG_USI_OVERFLOW
USI Overflow
USI_START_vect
SIG_USI_START
USI Start Condition
USI_STRT_vect
SIG_USI_START
USI Start
USI_STR_vect
SIG_USI_START
USI START
WATCHDOG_vect
SIG_WATCHDOG_TIMEOUT
Watchdog Time-out
WDT_OVERFLOW_vect
SIG_WATCHDOG_TIMEOUT, SIG_WDT_OVERFLOW
Watchdog Timer Overflow
WDT_vect
SIG_WDT, SIG_WATCHDOG_TIMEOUT
Watchdog Timeout Interrupt



AVR Tutorial - 3 Beginning AVR microcontroller Programming


1.                  Beginning AVR µC Programming

AVR is a RISC µC ie. Reduced Instruction Set Computing. So the number of instruction in the assembly for the µC will be much lesser as compared to CISC µCs. So it will take only lesser decoding time for instructions and will be comparatively faster for Embedded Systems.

1.        In this tutorial we will be using ATMEGA 32 and WinAVR Programming environment with avrdude as programming tool. We will be using USBasp as the AVR Programmer.
2.        If you are using any other AVR µC then just refer to the manual and see if there are some differences.
3.        ATMEGA 32 has got 32 kB flash memory for storing the program. It has got four  x  eight bit ports which they do label as PA, PB, PC, PD


·         Before going to the first example, let us get familiarized with the three types of basic port operating registers for AVR:
i.                    DDRx (x = A / B / C / D ): It is called Data Direction Register. It is used to set whether a particular bit in a port is being used for input or output. If we give a high value for a bit then that bit will be used for out putting something in the later programs.
Eg:                   DDRA = 0b00001111;
Here the first four bits (ie. bit 0 to bit 3) of port A is being used for outputting something and the last four(bit 4 to bit7) is being used for inputting something.
We could also write similarly in hexadecimal as:
                        DDRB = 0xf6;
ii.                   PORTx (x = A / B / C / D ): It is the Register which we use to output something into a port. As an example if we have set DDRA to have the bit 0 as output and if we want to set that to high we write as:
PORTB = 0x01;

iii.        PINx (x = A / B / C / D ): It is used to read value from a register. as an example if we want to check the input in the most significant four bits of port C, we do write the program with the use of bitwise operator as follows:
                        if( (PINC & 0b11110000) = = 0b101010000 )
{
                        -- do something --
                        }

The above usage of bitwise operator to mask the four lower bits which we are not concerned about is called bit masking. If we AND a bit with 0, it will become 0 and if we OR a bit with 1, it will become one. In the later on programming examples we will encounter the usage of bitwise operators a lot many times.

Example 1: Lighting an LED with µC

Circiut:

Program:
#include <avr/io.h>

void main()
{
  DDRB = 0b00000001;//Setting Bit 0 for Out put
  while(1)
  {
        PORTB = 0b00000001;//Setting Port B0 to High
  }
}

-          In the circuit given above, we have used a 8 MHz external crystal to clock the µC. There is an internal clock in a µC but its frequency is low limited (about 1MHz) and it is not precise. So we add an external crystal to the µC to make it run faster.
-          The output coming from the µC is 5V and the LED needs just a 2 to 3 V voltage. So we use a 330Ohm resistor to reduce the voltage.
-          Using the command PORT PORTB = 0b00000001; we do assign a high voltage to the 0th bit of PORTB and so the LED keeps on lighting.
-          Here you can see  unlike the usual C-Program which goes sequentially and terminated after certain number of steps, the programs for a µC are generally written inside an infinite while loop so that it gets executed infinitely.
-          Now we will change the code slightly with the same hardware to make the LED blink.

Program:
#include <avr/io.h>
#include <util/delay.h>

void main()
{
     DDRB = 0b00000001;
     while(1)
     {
                             PORTB = 0b00000001;
                             _delay_ms(100);
                             PORTB = 0b00000000;
                             _delay_ms(100);
    
     }
}

·         Here we are using a  _delay_ms( ) function which makes the µC do nothing for some milliseconds. So for using that function, we do include the header file delay.h
·         When we program this, we could see the LED blinking.


Now we will try to add an external switch to our circuit and do something…


Example 2: Adding a switch into µC
·         In the above circuit, we have connected a switch to the PINB1. If the switch is not being pressed, then a high voltage will be dropped across the resistor and the PINB1 will be having a logical high voltage.
·         If we press the switch then a logical 0 voltage will come to the PIN1 and we have added a capacitor across the switch to avoid de-bouncing.




Program:
#include <avr/io.h>
#include <util/delay.h>


void main()
{
           DDRB = 0b00000001; //Here we are setting PB0 for output
          
           while(1)
           {
                 if( (PINB & 0b00000010) == 0b00000000) //Checking whether switch is pressed
                       {
                                   PORTB = 0b00000001;
                                   _delay_ms(20);
                                   PORTB = 0b00000000;
                                   _delay_ms(20);
                       }
                       else
                       {
                                   PORTB = 0b00000001;
                                   _delay_ms(100);
                                   PORTB = 0b00000000;
                                   _delay_ms(100);
                       }
           }
}

We could make a slight modification in the program and we ccan give an internal pull up to avoid the external 10K pull up resistor.

#include <avr/io.h>
#include <util/delay.h>


void main()
{
           DDRB = 0b00000001; // setting PB0 for output
           PORTB = 0b00000010; //Setting the PINB1 with a high voltage
          
           while(1)
           {
                       PORTB |= 0b00000010;
                 if( (PINB & 0b00000010) == 0b00000000) //Checking whether switch is pressed
                       {
                                   PORTB = 0b00000001;
                                   _delay_ms(20);
                                   PORTB = 0b00000000;
                                   _delay_ms(20);
                       }
                       else
                       {
                                   PORTB = 0b00000001;
                                   _delay_ms(100);
                                   PORTB = 0b00000000;
                                   _delay_ms(100);
                      
                       }
          
           }


}

·         In the above program if we press the switch, then the LED will blink faster.





Interfacing LCD:
For interfacing LCD we will be using the header file lcd.h from http://www.jump.to/fleury and we will be calling the functions defined in that header file.

LCD Connection:

Vee is the Pin in the LCD used to set the contrast of the LCD. So use a resistance nearly 1 K with that to connect to Vcc.





·         Make sure to set your Clock speed properly in the lcd.h header file.

Sample Program:
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"
void main( )
{
lcd_init(LCD_DISP_ON_CURSOR); 
    while(1)                      
    {
        lcd_clrscr();             /* clear screen of lcd */
        lcd_home();               /* brings cursor to starting position */
        lcd_puts("Hello");        /*Prints on the first line */
        lcd_gotoxy(0,1);        
        lcd_puts("World");  /* Prints on the second line */
        _delay_ms(50);            /* wait 50ms */
    }
}



Integers and floating pont numbers are first converted to a string of characters and then they are printed using the usual lcd functions
Printing Integers
char temp[15];
int x = 246;
itoa(x, temp, 15);
lcd_puts(temp);

Printing Floating Point Numbers:
char temp[15];
float y = 56.7891;
sprintf(buffer, "%f", y);
lcd_puts(temp);