Sei sulla pagina 1di 8

IAR Application Note AVR-007, January 2000

,$5$SSOLFDWLRQ1RWH$95
(IILFLHQWSURJUDPPLQJRI$WPHO·V
$95PLFURFRQWUROOHUV

6XPPDU\
This application note provides some useful hints on how to program the
Atmel AVR microcontrollers using the IAR Compiler. There is a small
sample program for each topic enclosed with this application note.

.H\ZRUGV
hardware initialization, macros, flash, I/O, I/O registers

:KRVKRXOGUHDGWKLVDSSOLFDWLRQQRWH"
This application note describes some useful tips and tricks on how to program
the Atmel AVR range of microcontrollers. You should know how to:
• Create a project using the IAR Embedded Workbench
• Alter a linker command file (xcl file) to fit the application
• Set up and modify project build options.
You can download application notes on these topics from IAR’s and Atmel’s
websites (www.iar.com and www.atmel.com respectively).
There are also many other application notes on the Atmel AVR range of
microcontrollers at these sites, so check them out!
All C examples included in this application note can be used on the
AT STK200 Starter Kit for the AVR microcontrollers without modification.
The AT90S8515 is used for all samples.
If you intend to use the code samples in your own applications, or if you are
using another AVR device, please remember to make the required changes
(such as including another header file for the I/O definitions).

7RSLFV
This application note covers the following topics:
1. Hardware initialization
2. Using macros for frequently used instructions
3. Using flash library functions (flash keyword)
4. Using I/O registers for global variables.

1
IAR Application Note AVR-007, January 2000

7KHFLUFXLW
All examples presented in this application note use the following circuit:

Figure 1. Circuit diagram

Note
The complete diagrams of the starter kits for the AVR microcontrollers can be downloaded from
www.avr-forum.com .

/HW·VVWDUWFRGLQJ
1. Hardware initialization
The ATMEL AVR range of microcontrollers has many peripheral functions,
like timers, ports, serial interfaces, sleep modes, and an A/D Converter. All
these peripherals must be initialized prior to using them. This can be done in
different ways as described below.
1.1. Calling a setup function
The easiest way to initialize the controller is to write a function that includes
all setup instructions. The main program then calls this function:

void init(void) // initialization of controller


{
// Initialize I/O ports
DDRB = 0xff; // LEDs were connected to PORTB
// all PORTB pins are outputs
PORTB = 0xff; // switch off LEDs

DDRD = 0x00; // switches were connected to PORTD


// all PORTD pins are inputs
PORTD = 0xff; // pull ups enabled
// End
}

2
IAR Application Note AVR-007, January 2000

1.2. The library function __low_level_init()


The IAR Compiler offers an easy way of initializing the controller hardware
at an early stage. When a program starts executing, one of the first things it
does is to call the library function __low_level_init(). The default version of
this function, which is included in the library files, is as follows:

int __low_level_init(void)
{
return(1);
}

If a project contains a function with this specification, it will be used instead


of the library function. By adding this function you therefore ensure a quick
initialization of the hardware.

Remember to include the return(1); statement at the end of the function. If


zero is returned by the __low_level_init() function, the startup function will
not initialize variables, i.e. clear static data and copy from CDATA[0-3] to
IDATA[0-3].

The __low_level_init() function for our example looks like this:

int __low_level_init(void)
{
// Initialize I/O ports
DDRB = 0xff; // LEDs were connected to PORTB
// all PORTB pins are outputs
PORTB = 0xff; // switch off LEDs

DDRD = 0x00; // switches were connected to PORTD


// all PORTD pins are inputs
PORTD = 0xff; // pull ups enabled
// End

return(1); // startup function initializes variables


}

When you use the library function, the hardware is initialized before the main
program starts to execute. The difference between the methods becomes
obvious if you debug the demo projects Tutor_1, which uses the standard
function call, and Tutor_1_1, which uses __low_level_init().

3
IAR Application Note AVR-007, January 2000

 8VLQJPDFURVIRUIUHTXHQWLQVWUXFWLRQV

2.1. General information on macros


One of the most common tasks for embedded control applications is to check
the input to the application, for example when a push button is pressed and
when an I/O pin is set or cleared. Setting and clearing bits in the controller’s
I/O registers performs this input check.

The C instructions are:

PORTB |= (1<<PINB0); // set bit 0 of PORTB


// instruction will be compiled to SBI
// assembly instruction

PORTB &= ~(1<<PINB0); // clear bit 0 of PORTB


// instruction will be compiled to CBI
// assembly instruction

Note
The SBI and CBI assembly instructions operate on the lower 32 I/O registers of the AVR microcontrollers.

When the application has to react on input signals, you must check the I/O
registers. This is done in the following way:

if (PORTD & (1<<PIND0)) // check if bit 0 of PORTD is set


{
<do something>;
}

if (!(PORTD & (1<<PIND0))) // check if bit 0 of PORTD is clear


{
<do something>;
}

Since these instructions are used often, it is convenient to define them as


macros.

The following macros correspond to the instructions above:

// clear bit in I/O register


#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))

// set bit in I/O register


#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))

// test bit in I/O register


#define TESTBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

These macros are available in the included header file macros.h.

It is also possible to use macros to replace small functions, i.e. functions that
assemble into 3–4 lines or less of assembly code. The compiler generates less
code and gives higher speed when you use macros, as there is no need to call a
function.

The following example shows how the macros can be used:

4
IAR Application Note AVR-007, January 2000

//example of using the macros


if (TESTBIT(PORTB,PINB0)) // test if PINB0 is set
{
CLEARBIT(PORTB,PINB0); // clear bit 0 on PORTB
}

if (!(TESTBIT(PORTB,PINB0))) // test if PINB0 is clear


{
SETBIT(PORTB,PINB0); // set bit 0 on PORTB
}

Note
When using these macros, please alter your header files by commenting out these lines:
#ifdef __IAR_SYSTEMS_ASM
#endif /* __IAR_SYSTEMS_ASM (Bit definitions)*/

2.2. Writing your own macros


Writing macros is quite a simple task, but there are some design rules you
have to pay attention to:
• Macros are case sensitive, i.e. a macro named SETBIT is not the same
macro as sETBIT or SetBIT
• When you write macros with more than one instruction, each line must
end with a ‘\’.

// example of a macro with more than one instruction


#define MYMACRO /* comments */ \
{\
<first instruction>; /* comments */ \
<second instruction>; /* comments */ \
<third instruction>; /* comments */ \
}

 8VLQJIODVKOLEUDU\IXQFWLRQV
In nearly all kinds of applications there are some values which do not change
during the execution of the program. A common way to define these constants
is:

const char min_value = 25;


const char max_value = 250;

These constants are copied to the controller’s SRAM at startup and remain
there during the rest of the program execution. By defining constans this way
you will waste SRAM, because the constants are only accessed by some of
your functions.

A more efficient way to handle constants is to store them in flash memory and
only load them when they are needed. Use the flash keyword to declare the
constants. The constants can be accessed directly or by pointers.

flash char min_value = 25; // constant placed in flash


flash char max_value = 250; // constant placed in flash

5
IAR Application Note AVR-007, January 2000

For a constant table, which holds the LED pattern for our next example, this
would look as follows:

flash char led_patt[] = {255, 126, 189, 219, 231, 231, 219, 189, 126, 255};
// holds LED data

PORTB = led_patt[0]; // accessing led_patt array directly

char flash *patt_pointer = &led_patt[0]; // declare flash pointer to led_patt

PORTB = *patt_pointer; // accessing led_patt array by pointer


// PORTB = led_patt[0]

You can now use all C pointer operations on this pointer, i.e.:

// indirect addressing
PORTB = *patt_pointer;

// indirect addressing with post increment


PORTB = *patt_pointer++;

// indirect addressing with pre-decrement


PORTB = *--patt_pointer;

The IAR Compiler comes with a library of routines (pgmspace.h) which


allows access to strings in program memory. See the IAR Compiler Reference
Guide for details.

The enclosed example Tutor_3 shows how to use macros and the flash library
function.

 8VLQJ,2UHJLVWHUVIRUJOREDOYDULDEOHV
You should use local variables with the AVR IAR Compiler. This is because
local variables can be allocated in registers, whereas global variables will
always be stored in SRAM.

In some cases variables are global in nature but cannot be declared locally to a
function, e.g. interrupt functions that need to access variables.

Another possible scenario is that some single-bit flags must be stored in one
byte. Using a bitfield for this is not a good idea, because bitfields are not
stored in registers; they are popped and pushed on the stack each time they are
accessed. Because of this, the code generated by using bitfields is not very
efficient or fast.

When you take a look at the AVR’s I/O registers, you will see that there are
some locations which were not used by your application (e.g. your application
is not using the UART). Some of these locations can therefore be used for
storing global variables. This has some advantages compared to storing the
variable in SRAM:
• Directly accessible in a single cycle using one-word instructions
• Setting or clearing single bits using one-word instructions for I/O
registers 0-31

6
IAR Application Note AVR-007, January 2000

• Testing bits using single-cycle bit-test instructions for I/O registers 0-31.

The following example demonstrates the use of the UART baud-rate register
to store bitflags.

First you must define the bitflags. Create a new header file, for example
bit_flag1.h, with the following contents:

// the UBRR register of the AT90S8515 is at location 0x09


sfrb MSCGEFLAGS = 0x09

// bit definitions
#define MSC_GE7 7
#define MSC_GE6 6
#define MSC_GE5 5
#define MSC_GE4 4
#define MSC_GE3 3
#define MSC_GE2 2
#define MSC_GE1 1
#define MSC_GE0 0

By including the bit_flag1.h header file you can use the bitflags in your
application.
Note
Using names directly associated to your application (i.e. up, down, left, and right for a motion control
application) makes your code more transparent

// example for using bitflags


#include <io.8515.h>
#include <bit_flags1.h>

void testfunction(void)
{
MSCGEFLAGS |= (1<< MSC_GE0); // set bit 0 in UBRR

if(MSCGEFLAGS & (1<< MSC_GE0) // check if bit 0 in UBRR


// is set
MSCGEFLAGS &= ~(1<< MSC_GE0); // clear bit 0 in UBRR
}

The following I/O registers can be considered for this purpose, as long as the
corresponding I/O unit is not in use in your application:
• UART baud-rate register (UBRR)
• ADC multiplexer (ADMUX)
• EEPROM data register
• EEPROM address register low byte (EEARL)
• EEPROM address register high byte (EEARH)
• Timer/Counter registers (TCNTx).

Refer to your controller’s data sheet to determine how many bits can be stored
in these registers!

7
IAR Application Note AVR-007, January 2000

Some other registers can also be used, but require special attention. In each
case, you should carefully study the controller’s data sheet to ensure that the
normal operation of the device is not affected by the use of I/O registers for
storing global variables.

The example Tutor_3_1 demonstrates all topics covered in this application


note.

6XPPDU\
By using the methods described in this application note you will gain from the
following benefits:
• The hardware is initialized very fast using the __low_level_init() library
function.
• By using macros to replace small functions your code will be smaller and
faster.
To achieve this, perform the following steps:
1. Create a header file that includes all I/O connections of your application
by using the method described in part 4, and make access to the I/Os using
macros. If the design changes, i.e. you will have to use another I/O pin,
you will only need to modify the header file and not the entire program.
2. Use flash library functions when working with constants. This will
decrease the RAM requirement of your application.
3. If your application permits it, use I/O registers to store bit-flags. This will
make the generated code faster and more efficient.

Potrebbero piacerti anche