Interruptus Art

download Interruptus Art

of 9

Transcript of Interruptus Art

  • 7/30/2019 Interruptus Art

    1/9

    Interrupt Driven USART in AVR-GCC

    Dean Camera

    November 4, 2012

    **********

    Text Dean Camera, 2012. All rights reserved.

    This document may be freely distributed without payment to the author, provided that it is notsold, and the original author information is retained.

    For more tutorials, project information, donation information and author contact information,please visit www.fourwalledcubicle.com.

    1

    http://www.fourwalledcubicle.com/http://www.fourwalledcubicle.com/
  • 7/30/2019 Interruptus Art

    2/9

    Contents

    1 Introduction 3

    1.1 What are Interrupts? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

    2 A recap: The simple echo program 4

    3 Populating the ISR 6

    4 Enabling the USART Receive Interrupt 7

    5 Putting it all together 9

    2

  • 7/30/2019 Interruptus Art

    3/9

  • 7/30/2019 Interruptus Art

    4/9

    Chapter 2

    A recap: The simple echo program

    From the last serial tutorial, we have created a simple program from scratch which will echo bytesreceived on the AVRs USART interface. The full program listing is as follows:

    #include < a v r / i o . h >

    #define U S A R T _ B A UD R A T E 9 6 0 0#define B A U D_ P RE S C AL E ( ( ( F_ C PU / ( U S A R T_ B A UD R A TE * 1 6 U L) ) ) - 1 )

    in t m a in ( void ){

    char R e c e i v e d B y t e ;

    U CS RB | = ( 1 < < R X EN ) | ( 1 < < T XE N ); / / T u rn o n t he t r a ns m i ss i o n a n d r e ce p ti o n c i rc u it r yU CS RC | = ( 1 < < U R SE L ) | ( 1 < < U CS Z0 ) | ( 1 < < U CS Z1 ) ; / / U s e 8 - b i t c h ar a c te r s i ze s

    U B RR H = ( B A U D_ P R ES C A LE > > 8 ) ; / / L o a d u p p e r 8 - b i ts o f t h e b a ud r a te v a lu e i n to t h e h i g h b y t eo f t he U B RR r e gi s te r

    U B R R L = B A U D _ P R E SC A L E ; / / L oa d l ow er 8 - bi ts o f t h e b au d r at e v al ue i nt o t he l ow b yt e o f t h eU B R R r e g i s t er

    fo r (;;) / / L o op f o re v er{

    while ( ( UC SR A & ( 1 < < R XC ) ) = = 0 ) { } ; / / D o n o th i ng u n ti l d a ta h a ve b e en r e ce i ve d a n d i sr ea dy t o b e r ea d f ro m U DR

    R e c ei v e dB y t e = U D R ; / / F e tc h t h e r e c e i ve d b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "

    while ( ( UC SR A & ( 1 < < U DR E )) = = 0 ) { }; / / D o n ot hi ng u nt il U DR i s r ea dy f or m or e d at a t ob e w ri tt en t o i t

    U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i v ed b y te b a ck t o t h e c o mp u te r}

    }

    Readers should be able to fully understand this code if you cannot please re-read the previoustutorial on basic serial communication.

    Now, we want to extend this code so that the serial data is echoed back when received in aninterrupt, rather than our main program loop. To do this, first we need to include the avr-libcstandard library header, . This file contains library functions and macros

    which relate to the interrupt functionality of the AVR. Well add this to the top of our code, belowthe standard device header, include:

    #include < a v r / i o . h >#include < a v r / i n t e r r u p t . h >

    Once included, we now have a way to make our ISR to deal with the serial reception. To make anISR, we use the syntax:

    I S R ( { V e c t or N a m e } , { O p t i o n s } ){

    / / C o d e t o b e e x e c u te d w h en I S R f i r es

    }

    4

  • 7/30/2019 Interruptus Art

    5/9

    CHAPTER 2. A RECAP: THE SIMPLE ECHO PROGRAM 5

    And place it in our program as if it was a normal function. To add one to deal with the receptionof a byte via the USART, we need to look for the appropriate name in our AVRs datasheet. In thedatasheet for our example AVR, the ATMEGA16, we see that the name of the interrupt for whena byte is received is USART RXC. The standard avr-libc device header file includedin our program as well as any other AVR-GCC program involving the AVRs I/O functionality

    defines the vector names for us.

    The avr-libc symbolic names for each of the interrupt vectors is identical to the datasheet, withthe addition of a vect suffix to denote that it is a vector name. So, as our datasheet listedUSART RXC as the vector name, the syntax for our program is:

    I S R ( U S A R T_ R X C _ ve c t , I S R _ B LO C K ){

    / / C o d e t o b e e x e c u te d w h en t h e U S A RT r e ce i ve s a b y te h e re}

    Which well place at the end of our program, after our main function. Note that here Ive used

    the ISR option of ISR BLOCK, which is what most interrupt service routines should use except inspecial situations. Thats outside the scope of this tutorial, but please read through the avr-libcmanual if you wish to know more about this.

    The new program code looks like this:

    #include < a v r / i o . h >#include < a v r / i n t e r r u p t . h >

    #define U S A R T _ B A UD R A T E 9 6 0 0#define B A U D_ P RE S C AL E ( ( ( F_ C PU / ( U S A R T_ B A UD R A TE * 1 6 U L) ) ) - 1 )

    in t m a in ( void ){

    char R e c e i v e d B y t e ;

    U CS RB | = ( 1 < < R X EN ) | ( 1 < < T XE N ); / / T u rn o n t he t r a ns m i ss i o n a n d r e ce p ti o n c i rc u it r yU CS RC | = ( 1 < < U R SE L ) | ( 1 < < U CS Z0 ) | ( 1 < < U CS Z1 ) ; / / U s e 8 - b i t c h ar a c te r s i ze s

    U B RR H = ( B A U D_ P R ES C A LE > > 8 ) ; / / L o a d u p p e r 8 - b i ts o f t h e b a ud r a te v a lu e i n to t h e h i g h b y t eo f t he U B RR r e gi s te r

    U B R R L = B A U D _ P R E SC A L E ; / / L oa d l ow er 8 - bi ts o f t h e b au d r at e v al ue i nt o t he l ow b yt e o f t h eU B R R r e g i s t er

    fo r (;;) / / L o op f o re v er{

    while ( ( UC SR A & ( 1 < < R XC ) ) = = 0 ) { } ; / / D o n o th i ng u n ti l d a ta h a ve b e en r e ce i ve d a n d i sr ea dy t o b e r ea d f ro m U DR

    R e c ei v e dB y t e = U D R ; / / F e tc h t h e r e c e i ve d b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "

    while ( ( UC SR A & ( 1 < < U DR E )) = = 0 ) { }; / / D o n ot hi ng u nt il U DR i s r ea dy f or m or e d at a t ob e w ri tt en t o i t

    U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i v ed b y te b a ck t o t h e c o mp u te r}

    }

    I S R ( U S A R T _ R X C _ v e c t ){

    / / C o d e t o b e e x e c u te d w h en t h e U S A RT r e ce i ve s a b y te h e re}

  • 7/30/2019 Interruptus Art

    6/9

    Chapter 3

    Populating the ISR

    At the moment our new USART reception ISR doesnt actually do anything weve just definedit. We want it to echo back the byte that is sent, so well move our main loop code:

    while ( ( UC SR A & ( 1 < < R XC ) ) = = 0 ) { } ; / / D o n o th i ng u n ti l d a ta h a ve b e en r e ce i ve d a n d i s r e ad yt o b e r ea d f ro m U DR

    R e c ei v e dB y t e = U D R ; / / F e tc h t he r e c ei v ed b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "

    while ( ( UC SR A & ( 1 < < U DR E )) = = 0 ) { }; / / D o n ot hi ng u nt il U DR i s r e a dy f or m or e d at a t o b ew r it t en t o i t

    U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i ve d b y te b a ck t o t h e c o mp u te r

    Over to it. However, we can now remove the two while loops since the ISR only fires when abyte is received, and only one byte is sent after each reception, we can guarantee that both checksare now redundant. When the ISR fires we know that there is both a byte received in the USARTinput buffer, as well as nothing in the output buffer. Using this knowledge, we can simplify ourISR code to the following:

    I S R ( U S A R T _ R X C _ v e c t )

    {char R e c e i v e d B y t e ;R e c ei v e dB y t e = U D R ; / / F e tc h t he r e c ei v ed b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i ve d b y te b a ck t o t h e c o mp u te r

    }

    Note that Ive also moved the variable declaration of the variable ReceivedByte over to the ISR,as that is now where it is actually used.

    Its worth mentioning at this point a small section on the datasheet about the RXC interrupt:

    When interrupt-driven data reception is used, the receive complete routine must read

    the received data from UDR in order to clear the RXC Flag, otherwise a new interruptwill occur once the interrupt routine terminates.

    Thats important for us to remember: if you are using the USART RXC interrupt, you must reada byte from the UDR register to clear the interrupt flag. We do that in our above code, but keep itin mind for your future projects!

    6

  • 7/30/2019 Interruptus Art

    7/9

    Chapter 4

    Enabling the USART Receive In-

    terrupt

    Lets take a look at the latest incarnation of our test code:

    #include < a v r / i o . h >#include < a v r / i n t e r r u p t . h >

    #define U S A R T _ B A UD R A T E 9 6 0 0#define B A U D_ P RE S C AL E ( ( ( F_ C PU / ( U S A R T_ B A UD R A TE * 1 6 U L) ) ) - 1 )

    in t m a in ( void ){

    U CS RB | = ( 1 < < R X EN ) | ( 1 < < T XE N ); / / T u rn o n t he t r a ns m i ss i o n a n d r e ce p ti o n c i rc u it r yU CS RC | = ( 1 < < U R SE L ) | ( 1 < < U CS Z0 ) | ( 1 < < U CS Z1 ) ; / / U s e 8 - b i t c h ar a c te r s i ze s

    U B RR H = ( B A U D_ P R ES C A LE > > 8 ) ; / / L o a d u p p e r 8 - b i ts o f t h e b a ud r a te v a lu e i n to t h e h i g h b y t eo f t he U B RR r e gi s te r

    U B R R L = B A U D _ P R E SC A L E ; / / L oa d l ow er 8 - bi ts o f t h e b au d r at e v al ue i nt o t he l ow b yt e o f t h eU B R R r e g i s t er

    fo r (;;) / / L o op f o re v er{

    / / D o n ot hi ng - e ch oi ng i s h an dl ed b y t h e I SR i ns te ad o f i n t he m ai n l oo p

    }}

    I S R ( U S A R T _ R X C _ v e c t ){

    char R e c e i v e d B y t e ;R e c ei v e dB y t e = U D R ; / / F e tc h t he r e c ei v ed b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i ve d b y te b a ck t o t h e c o mp u te r

    }

    If you compile and run this, youll notice that nothing happens no characters are echoed backto the connected computer. This is because although weve defined and populated the ISR, wehavent enabled it. To do so, we need to do two things:

    1. Turn on global interrupts

    2. Enable the USART Byte Received interrupt

    Item one is simple, so well do that first. The AVR microcontrollers contain a global flag which canbe set or cleared to enable or disable the handling of interrupts. Note that setting this flag doesntenable all interrupts, it only allows for the possibility of running them. If the Global InterruptEnable flag is disabled, all interrupts will be ignored, even if they are enabled (more on that later).

    To turn on the Global Interrupt Enable flag, we can use the macro sei() which the avr-libc headerfile helpfully defines for us. This is so named as it generates a SEI assemblyinstruction in the final code listing, which the AVR interprets as an order to set the Global Interrupt

    Enable flag. The compliment of sei() is cli() (to turn off the handling of interrupts) howeverwe will not be using that macro in this tutorial.

    7

  • 7/30/2019 Interruptus Art

    8/9

    CHAPTER 4. ENABLING THE USART RECEIVE INTERRUPT 8

    Well add our sei() instruction to our main routine, after configuring the USART registers:

    / / . . .U B RR H = ( B A U D_ P R ES C A LE > > 8 ) ; / / L o a d u p p e r 8 - b i ts o f t h e b a ud r a te v a lu e i n to t h e h i g h b y t e

    o f t he U B RR r e gi s te rU B R R L = B A U D _ P R E SC A L E ; / / L oa d l ow er 8 - bi ts o f t h e b au d r at e v al ue i nt o t he l ow b yt e o f t h e

    U B R R r e g i s t er

    sei(); / / E n ab l e t he G l ob a l I n te r r up t E n ab l e f l ag s o t h at i n te r r up t s c a n b e p r oc e s se d

    fo r (;;) / / L o op f o re v er/ / . . .

    Now for item two on our list, which needs to be performed before the interrupt will be enabled.We need to specifically enable the USART Receive Complete interrupt, which we can do by settingthe appropriate flag in the USART control register.

    In the ATMEGA16, this bit is called RXCIE (short for Recieve Complete Interrupt Enable) andis part of the register UCSRB. Setting this bit enables the handling of the USART RXC event vector:

    U C SR B | = ( 1 < < R X C I E ) ;

    Well add this to our main routine, before our new sei(); command.

  • 7/30/2019 Interruptus Art

    9/9

    Chapter 5

    Putting it all together

    Now we have a working interrupt driven serial example:

    #include < a v r / i o . h >#include < a v r / i n t e r r u p t . h >

    #define U S A R T _ B A UD R A T E 9 6 0 0

    #define B A U D_ P RE S C AL E ( ( ( F_ C PU / ( U S A R T_ B A UD R A TE * 1 6 U L) ) ) - 1 )

    in t m a in ( void ){

    U CS RB | = ( 1 < < R X EN ) | ( 1 < < T XE N ); / / T u rn o n t he t r a ns m i ss i o n a n d r e ce p ti o n c i rc u it r yU CS RC | = ( 1 < < U R SE L ) | ( 1 < < U CS Z0 ) | ( 1 < < U CS Z1 ) ; / / U s e 8 - b i t c h ar a c te r s i ze s

    U B RR H = ( B A U D_ P R ES C A LE > > 8 ) ; / / L o a d u p p e r 8 - b i ts o f t h e b a ud r a te v a lu e i n to t h e h i g h b y t eo f t he U B RR r e gi s te r

    U B R R L = B A U D _ P R E SC A L E ; / / L oa d l ow er 8 - bi ts o f t h e b au d r at e v al ue i nt o t he l ow b yt e o f t h eU B R R r e g i s t er

    U C SR B | = ( 1 < < R X C I E ); / / E n ab l e t he U S AR T R e ci e ve C o mp l et e i n te r r up t ( U S A RT _ RX C )sei(); / / E n ab l e t he G l ob a l I n te r r up t E n ab l e f l ag s o t h at i n te r r up t s c a n b e p r oc e s se d

    fo r (;;) / / L o op f o re v er{

    / / D o n ot hi ng - e ch oi ng i s h an dl ed b y t h e I SR i ns te ad o f i n t he m ai n l oo p}

    }

    I S R ( U S A R T _ R X C _ v e c t ){

    char R e c e i v e d B y t e ;R e c ei v e dB y t e = U D R ; / / F e tc h t he r e c ei v ed b y te v a lu e i n to t he v a ri a b le " B y t eR e c ei v e d "U D R = R e c ei v e dB y t e ; / / E c h o b a ck t he r e ce i ve d b y te b a ck t o t h e c o mp u te r

    }

    Which, like out original program, will echo characters received via the USART. However, becauseour program is now interrupt driven, we can add in code into the main loop which will be executedwhen data is not received such as flashing a LED.

    Interrupts allow for infrequent background tasks to be executed when they occur, without posinga run-time penalty of having to poll the hardware until the even occurs. This frees up our mainloop to take care of the critical code, with the interrupt code pausing the main code to executewhen the event of interest ocurs.

    Interrupts should be made to be as short as possible in execution time. This is because while oneISR is executing, others are blocked and thus if another ISR condition occurs while one ISR isexecuting, that ISR event will be missed or delayed.

    Because of this, communication ISRs are generally made short by receiving in characters via anISR, and placing them into a buffer which the main code may read at its leisure. This ensuresthat received data will not be missed, while giving the main program time to complete what it iscurrently doing before having to process the input. Similarly, long transmissions may be made byplacing the data to be sent into a buffer, and have an interrupt send the data as the hardware isready while the main program performs other tasks.

    9