avr-gcc-list
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-gcc-list] AVR Gcc with AVR Tiny


From: David Brown
Subject: [avr-gcc-list] AVR Gcc with AVR Tiny
Date: Tue, 16 Aug 2005 16:31:48 +0200

I've just recently written a program for an AVR Tiny 12 using avr-gcc, and
thought others might be interested.  If there is enough interest, it might
be worth adding information to the documentation or even modifying the
tools.

I used a fairly recent winavr setup (gcc 3.4.1).

As it stands, avr-gcc refuses to compile C for the -mmcu=avr1 architecture,
although assembly and linking should work fine.  Thus the trick to getting C
to work is mainly a matter of fooling the compiler into aiming for an avr2
target (the simplest AVR with ram), and avoiding any constructs that might
use actual ram.

I used a single C file - tiny programs are rarely large, and it gives the
compiler better optomisation opportunities.  You want functions to be
inlined where possible, since the call stack is only 3 entries deep (one of
which is used for interrupts), and since it lets the compiler make better
use of the registers.

Any global registers needed must be declared with something like:
    register unsigned char globalCounter asm("r2");
to force them into a register.

It's also worth remembering that hardware registers can store data - for
example, if you are not using eeprom then the eeprom data register is a
perfectly good place to store a global byte.  There are likely to be plenty
of single bits lying around in I/O space that are not otherwise used.

Because you are fooling the compiler about the architecture, you can't just
#include the usual <avr/io.h> file.  I found it easiest to simply copy the
"tiny12.h" header into the project directory and #include it directly.  For
some reason, the eeprom registers were missing, so I had to define them
manually.

Building the project was more complex than usual - normally I just call
avr-gcc for compiling, assembling and linking, but here I called them
explicitly (avr-gcc, avr-as, avr-ld) for each stage to get complete control.
Once you've figured out what you need, it all goes in the makefile anyway.

For compiling, use -mmcu=avr2 and -O2 or -Os optomisation (or even -O3), and
"-s" to generate an assembly file.  Before assembling, I used sed to change
the "avr2" line to "avr1", with the command:
    sed -i 's/.arch avr2/.arch avr1/' program.s
Then you can call the assembler avr-as with "-mmcu=avr1" to generate the
object file.  This gives you a check for illegal instructions - anything
involving ram will cause an error here.

Linking is also a bit different.  I copied the avr1.x linker file into the
project directory and linked with:
    avr-ld -n -Map=prog.map -nostdlib -t -T avr1.x -m avr1 -o prog.elf
program.o
The key thing here are that no standard library files are linked, nor is
there any startup code.  You have complete control (and complete
responsibility!).

At the start of the main program file, I have the routine:

void start(void) __attribute__((naked, used));
void start(void) {
 asm volatile("; Vectors" ::);
 asm volatile("rjmp reset" ::);
 asm volatile("reti ; interrupt 0" ::);
 asm volatile("reti ; pin change" ::);
 asm volatile("rjmp timer0" ::);
}

This gets linked at the start of flash, so the function "reset" is called at
reset - there is no "main".  Two of the interrupts here have a simple
"reti" - this lets me use them to wake up the processor from sleep mode,
without actually executing any code.  "timer0" is a real interrupt routine.
The real interrupt routine should be as unobtrusive as possible, and be a
"naked" function.  An example is:

void timer0(void) __attribute__((naked));
void timer0(void) {
    globalCounter++;
    asm volatile("reti" ::);
}

Don't let the compiler save your registers in ram !

The program itself starts running at "reset", which again is a "naked"
function:

void reset(void) __attribute__((naked, noreturn, used));
void reset(void) {
    asm volatile("clr __zero_reg__" ::);
    // rest of program
}

Clearing the __zero_reg__ is important - without that, the program simulates
fine in AVR Studio, but fails in the real device (which starts up with a
random value in r1).  Remember also to initialise global variables as
needed.

All in all, there are a few hurdles to getting things running, but once I'd
figured out the details, it worked fine.  The Tiny 12 is a nice little
chip - small and compact, with very low power consumption (power down
current is much lower than battery leakage currents).

David

"I love deadlines.  I love the whooshing noise they make as they go past."
Douglas Adams






reply via email to

[Prev in Thread] Current Thread [Next in Thread]