A Basic Flasher Example

This program uses the STK300 megaAVR kit to demonstrate a number of basic features of the avrgcc C compiler and the Mega103 cpu. The code firstly initialises the A and B output ports; one line of PORTB is used to flash a LED, and one line of PORTA shows the state of the CPU power as controlled by the sleep instruction in Idle power down mode.

It also demonstrates using the SIGNAL interrupt type to respond to Timer0 overflow interrupts. Note you would not normally use such a simple timeout method in a real application with other interrupt sources, as any of these would bring the cpu out of the sleep Idle mode. More realistically the Timer0 interrupt would set a bit in a global flag variable, and the Timer function would check this as it comes out of sleep mode. For example:

   while (count--) {
      do {
         asm ("sleep"::);              // always go to sleep
      } while (!(Flag & TIMER0_BIT));  // loop if not Timer0
      flag &= ~TIMER0_BIT;             // clear bit
   }   
}

Here the do/while loop keeps processing flow within the outside count loop until the reason for coming out of sleep mode was actually a Timer0 interrupt. The Timer0 interrupt function would have an additional statement:

SIGNAL(SIG_OVERFLOW0)
{
   outp(CLK0_COUNT, TCNT0);   // reset the counter
   Flag |= TIMER0_BIT;        // flag the interrupt occurred
}

and the Flag variable would be declared somewhere as a global. Remember it must be declared volatile as it is updated by an interrupt routine and thus outside the knowledge of the compiler as it analyses normal program flow:

volatile unsigned int Flag;

You can cut and paste the source code and makefile from here into your favourite editor. The makefile is based on an original version from Volker Oth.


Basic Flasher Source

Save this as a C source file. To use the makefile below without any changes, save this as test.c.

/********************************************************************

  The STK300 equivelent of "Hello world". Demonstrates gcc
  interrupts and a number of AVR features.
  
  Ron Kreymborg
  
********************************************************************/

#include <wdt.h>
#include <io.h>
#include <signal.h>
#include <interrupt.h>
#include <stdlib.h>

// Definitions...
#define   CLK0_DIVIDER     0x04      // PCK0 / 64
#define   CLK0_COUNT       256-8     // 15.625 mSec tick
#define   ST_CLOCK_TICK    0x01      // system clock tick has occurred
#define   ON_TICKS         32        // LED on time = 32 * 15.625 = 500 mSec
#define   OFF_TICKS        32        // LED off time
#define   LED              1         // LED on PORTB pin 1

// Function prototypes...
int main(void);
void TurnOn(void);
void TurnOff(void);
void Timer(int count);



int main(void)
{
   // Ensure watchdog is off
   //
   wdt_disable();
   
   // Only program the A nd B ports here.
   //
   outp(0xff, PORTB);           // all LEDs off
   outp(0xff, DDRB);            // all output
   outp(0xff, PORTA);           // all lines high
   outp(0xff, DDRA);            // all output
   
   // Setup the Timer0 to use the external 32,768 Hz crystal
   // ticking every 15.625 mSecs (for a 4Mhz crystal).
   //
   sbi(ASSR, AS0);              // clock from external crystal
   outp(CLK0_DIVIDER, TCCR0);   // 32768 / 64 = 512 ticks/sec   
   outp(CLK0_COUNT, TCNT0);
   sbi(TIMSK, TOIE0);           // enable timer interrupt
   
   // Setup sleep to select Idle Mode
   //
   cbi(MCUCR, SM0);
   cbi(MCUCR, SM1);
   sbi(MCUCR, SE);
   
   // Hello world
   //
   sei();      
   
   // Main loop. Turn a LED on for ON_TICKS clock ticks, and
   // off for OFF_TICKS ticks.
   //
   while (1)
   {
      TurnOn();
      TurnOff();
   }
   
   return 0;
}

//-------------------------------------------------------------------
// Turn the LED on.
//
void TurnOn(void)
{
   cbi(PORTB, LED);
   Timer(ON_TICKS);
}

//-------------------------------------------------------------------
// Turn the LED off.
//
void TurnOff(void)
{
   sbi(PORTB, LED);
   Timer(OFF_TICKS);
}

//-------------------------------------------------------------------
// A scope on PORTA pin 1 should show the cpu being powered up for
// around 3 uSec every 15.625 mSec as it moves through the while 
// loop here. At every timeout period you should see the processor 
// run for around 10 uSec as it moves to the next state.
//
void Timer(int count)
{
   while (count--)
   {
      cbi(PORTA, 1);           // low means asleep
      asm ("sleep"::);   
      sbi(PORTA, 1);
   }   
}

//-------------------------------------------------------------------
// Timer0 interrupt - Ticks at 15.625 mSecs. 
//
SIGNAL(SIG_OVERFLOW0)
{
   outp(CLK0_COUNT, TCNT0);   // simply reset
}

Basic Flasher make File

This should be in the same directory as the source file above. It assumes the source file is saved as test.c.

# Simple Makefile
# Ron Kreymborg
#
# Define some variables based on the AVR base path in $(AVR)
   AVR      = c:/avrgcc
   CC       = avr-gcc
   AS       = avr-gcc -x assembler-with-cpp   
   RM       = rm -f
   BIN      = c:/avrgcc/bin/avr-objcopy
   INCDIR   = .
      
# Output format can be srec, ihex (avrobj is always created)
   FORMAT = srec

# Default compiler flags
   CPFLAGS   = -g -O3 -Wall -Wstrict-prototypes -Wa,-ahlms=$(<:.c=.lst)
   
# Default assembler flags
   ASFLAGS = -Wa,-gstabs
   
# Default linker flags
   LDFLAGS = -Wl,-Map=$(TRG).map,--cref
   
# Put the name of the target mcu here (at90s8515, at90s8535, attiny22, atmega603 etc.)
   MCU = atmega103

# Put the name of the target file here (without extension)
   TRG = test
   
# List your C sourcefiles here 
   SRC=$(TRG).c

# Put additional assembler source file here
   ASRC  =

# Additional libraries and object files to link
   LIB   =  

# Additional includes to compile
   INC   = 

# Compiler flags
   CPFLAGS = -g -O -Wall -Wstrict-prototypes -Wa,-ahlms=$(<:.c=.lst) -fverbose-asm -save-temps

# Assembler flags
   ASFLAGS = -gstabs

# Linker flags
   LDFLAGS = -Wl,-Map=$(TRG).map,--cref -lm

# Define all project specific object files
   OBJ   = $(ASRC:.s=.o) $(SRC:.c=.o) 
   CPFLAGS += -mmcu=$(MCU)
   ASFLAGS += -mmcu=$(MCU)
   LDFLAGS += -mmcu=$(MCU)
        
# This defines the aims of the make process
all:   $(TRG).obj $(TRG).elf $(TRG).rom $(TRG).eep

# Compile: instructions to create assembler and/or object files from C source
%o : %c 
   $(CC) -c $(CPFLAGS) -I$(INCDIR) $< -o $@

%s : %c
   $(CC) -S $(CPFLAGS) -I$(INCDIR) $< -o $@

# Assemble: instructions to create object file from assembler files
%o : %s
   $(AS) -c $(ASFLAGS) -I$(INCDIR) $< -o $@

# Link: instructions to create elf output file from object files
%elf: $(OBJ)
   $(CC) $(OBJ) $(LIB) $(LDFLAGS) -o $@

# Create avrobj file from elf output file
%obj: %elf
   $(BIN) -O avrobj -R .eeprom $< $@

# Create bin (ihex, srec) file from elf output file
%rom: %elf
   $(BIN) -O $(FORMAT) -R .eeprom $< $@

# Create eeprom load module from elf
%eep: %elf
   $(BIN) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Make instruction to delete created files
clean:
   $(RM) $(OBJ)
   $(RM) $(SRC:.c=.s)
   $(RM) $(SRC:.c=.lst)
   $(RM) $(TRG).map
   $(RM) $(TRG).elf
   $(RM) $(TRG).obj
   $(RM) $(TRG).eep
   $(RM) $(TRG).rom
   $(RM) *.bak
   $(RM) *.log

# Dependencies, add any dependencies you need here...

$(TRG).o : $(TRG).c

Ron Kreymborg, 12-Apr-2001