Binary Clock: RTC of ATmega48 and LED DISPLAY

sun_ge@yahoo.cn

Image binclock

Project Origin

A few years ago, I found some kinds of binary clock for sale in www.thinkgeek.com , then I tried to write a J2ME clock software that can show time by flashing green dot in my cell phone. When I knew the advantage of microcontroller just as ATmega48, I think it is very easy to make a hardware binary clock because:
  1. ATmega48 has a asynchronous T/C2 which can use a 32768Hz watch crystal as independent clock source.
  2. It has sufficient GPIO to drive a 8x8 LED dot-matrix display and three push button.
  3. ATMEL provide a application note AVR134: Real-Time Clock using the Asynchronous Timer  and example code.
  4. It has another project "LCD Big-Number Clock" in the Internet, Its code can be useful.

Schematics

You can enlarge it but I should confess that it's truely very ugly.

Instead of the schematics, source code also contain connection informations and technical notes, please see it for details.

Image binclock_sch

Source Code

Source contains: rtcasync.c, rtcasync.h, binclock.c and Makefile.
It can be compiled and installed by AVR GNU Tools Chain.

RTC Library: rtcasync.c


/*
rtcasync.c:
library to use mega48 async RTC of TC2.
including ISR.
*/

#include "rtcasync.h"

/* test for leap year */
uint8_t not_leap(void)
{
  if (!(year%100))
    return (uint8_t)(year%400);
  else
    return (uint8_t)(year%4);
}

/* 
Start Timer/Counter2 in asynchronous operation using a 32.768kHz crystal.
*/
void RTC_init(void)
{
  uint8_t sreg;
	
  /* Save status register */
  sreg=SREG;
  /* disable global interrupt */
  cli();
	
  /* 0. Oscillator might take as long as one second to stabilize. */
  _delay_ms(1000);
    
  /* 1. Disable the Timer/Counter2 interrupts by clearing OCIE2 and TOIE2. */
  TIMSK2 &= ~((1<<OCIE2A)|(1<<OCIE2B)|(1<<TOIE2));
  
  /* 2. Select clock source by setting AS2 as appropriate. */
  ASSR = (1<<AS2);
  
  /* 3. Write new values to TCNT2, OCR2, and TCCR2B. */
  TCNT2 = 0;
  // select precaler: 32.768 kHz / 128 = 1 sec between each overflow:
  TCCR2B |= (1<<CS22) | (1<<CS20);            
	
  /* 4. To switch to asynchronous operation: Wait for clear: TCN2UB, OCR2AUB, OCR2BUB, TCR2AUB and
     TCR2BUB. (p138) */
  while(ASSR & 0x1F);
	
  /* 5. Clear the Timer/Counter2 Interrupt Flags. */
  TIFR2 |= ((1<<OCF2A)|(1<<OCF2B)|(1<<TOV2));
	
  /* 6. enable Timer2 Overflow interrupt */
  TIMSK2 |= (1<<TOIE2);
    
  /* restore status-register */
  SREG=sreg;
  
  sei(); /* ensure enable interrupts */
}


void RTC_set(uint8_t th, uint8_t tm, uint8_t ts, uint16_t yyyy, uint8_t mm, uint8_t dd, uint8_t w)
{
  uint8_t sreg;
	
  sreg=SREG;
  cli();
	
  RTC_h=th; 
  RTC_m=tm; 
  RTC_s=ts;
  year=yyyy;
  month=mm;
  date=dd;
  week=w;
  
  //restore SREG
  SREG=sreg;
}

/*
 Timer2-Overflow ISR called every second
*/
ISR(TIMER2_OVF_vect)
{
	
  RTC_s++;
	
  if (RTC_s == 60)
    {
      RTC_s = 0;
      RTC_m++;
		
      if (RTC_m > 59)
	{
	  RTC_m = 0;
	  RTC_h++;
			
	  if (RTC_h > 23)
	    {
	      RTC_h = 0;

	      week++;
	      if (week > 7)
		week=1;
	      
	      date++;
	      
	      if (date > 31)
		{
		  date = 1;
		  month++;
		}
	      else if ((date == 31) && (month == 4 || month == 6 || month == 9 || month == 11))
		{
		  date=1;
		  month++;
		}
	      else if (date == 30 && month == 2)
		{
		  date=1;
		  month++;
		}
	      else if (date == 29 && month == 2 && not_leap())
		{
		  date=1;
		  month++;
		}
	      
	      if (month == 13)
		{
		  year++;
		  month=1;
		}
	      
	    }
	}
    }
}

RTC Library Header: rtcasync.h


#include <inttypes.h>
#include <avr/interrupt.h>

#define F_CPU 1000000UL
#include <util/delay.h>

volatile uint8_t RTC_h, RTC_m, RTC_s, month, date, week;
volatile uint16_t year;

/*if year is leap year, then return 0; else(not leap year) return non-zero*/
uint8_t not_leap(void);

/*set initial global value for start runing time.*/
void RTC_set(uint8_t th, uint8_t tm, uint8_t ts, uint16_t yyyy, uint8_t mm, uint8_t dd, uint8_t w);

/*Start Timer/Counter2 in asynchronous operation using a 32.768kHz crystal.*/
void RTC_init(void);

Main Program: binclock.c


/*************************************************************************
 mega48 led-dot-matrix RTC clock.
hardware:
1)8x8 led-dot-matrix connected. 
PORTD for column output(setting values),
and PORTB(0-5) and PINC4, PINC5 for row activate(scan).
2) PINC0 -- minus key
PINC1 -- plus key
PINC2 -- adjust select key.
3) PB6(TOSC1) and PB7(TOSC2) connected to a 32.768kHz crystal.

ABOUT VTG:
old comment:
mega48 can work on 0-10MHz@2.7-5.5V, but to 8x8 led-dot-matrix,
when I don't connect resisters between row(or column) and the PINs of AVR,
5V voltage will make the led-dot-matrix works terrible, AND if SCK, MISO,
MOSI also connect to the led-dot-matrix, ISP program will fail.
through a test, 3.0V will work better.(set the vtarg of stk500 to 2.7V or
use two AA alakine batteries.), and ISP program will succeed.
20060530:
ISP use stk500v2@5V is OK!
**************************************************************************/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "rtcasync.h"

/* use interal RC osililator 8 MHz. prescale 8MHz/8, lfuse=0x62 (default)*/
#define F_CPU 1000000UL
#include <util/delay.h>

/* Keep activated for 2 ms per column 
   if set to 3ms, the performance is terriable.*/
#define LIGHTED_TIME 2

/* remember that PINB6 is TOSC1 and PINB7 is TOSC2 */
#define KEYPORT PORTC
#define KEYDDR  DDRC
#define KEYPIN  PINC
#define KEYMINUS PINC0
#define KEYPLUS  PINC1
#define KEYSELECT PINC2

#define KEYCNTMAX  0xf0
/* <5*10ms key-pressed is bounce */
#define KEYCNTMIN  5
/* 100*10ms key-pressed is "long" */
#define KEYCNTLONG 100
/* check every 50*10 ms for key-press to update time */
#define DTCHECKKEY 50

volatile uint8_t gHeartBeat10ms = 0;
volatile uint8_t gKeyPlusCnt = 0, gKeyMinusCnt = 0, gKeySelectCnt = 0;

volatile uint8_t adjust=0; 
/* adjust sequence: time(0)->date, month(1)->year(2)->week(3)->
   display off(4)->time(0)...*/
/*define adjust flag*/
#define ADJ_MMHH 0
#define ADJ_DDMM 1
#define ADJ_Y 2
#define ADJ_W 3
#define ADJ_DISP_OFF 4

/*clock tick for key press, very 5ms*/
ISR(TIMER1_COMPA_vect)
{
  /* check if "minus"-key is pressed (active low) */
  if ( !(KEYPIN & (1<<KEYMINUS)) ) {
    if (gKeyMinusCnt < KEYCNTMAX) gKeyMinusCnt++;
  }
  else gKeyMinusCnt = 0;
	
  /* check if "plus"-key is pressed (active low) */
  if ( !(KEYPIN & (1<<KEYPLUS)) ) {
    if (gKeyPlusCnt < KEYCNTMAX) gKeyPlusCnt++;
  }
  else gKeyPlusCnt = 0;

  /* check if "select"-key is pressed (active low) */
  if ( !(KEYPIN & (1<<KEYSELECT)) ) {
    if (gKeySelectCnt < KEYCNTMAX) gKeySelectCnt++;
  }
  else gKeySelectCnt = 0;

	
  gHeartBeat10ms++;
}

void init_keys(void)
{
  uint8_t sreg;

  KEYDDR &= ~( (1<<KEYMINUS) | (1<<KEYPLUS) | (1<<KEYSELECT)); /* set pins as input */
  KEYPORT |= ( (1<<KEYMINUS) | (1<<KEYPLUS) | (1<<KEYSELECT)); /* enable int. pull-ups */
	
  sreg=SREG;
  cli();
	
  /* 
     use T/C1 to generate clock tick for key press.
     init T/C1 of CTC mode:
     
     f_tc1=1MHz/1024=1KHz; so,
     f_isr=f_tc1/OCR1A=1KHz/5=200Hz,
     T_isr=1/f_isr=1/200Hz=5ms.
     set top to 5, so that T of ISR is 5ms
  */
  OCR1A  = 5;
  
  /* CTC Mode 4 Prescaler 1024 */
  TCCR1B = (1<<WGM12)|(1<<CS12)|(1<<CS10); 
  
  TIMSK1 |= (1<<OCIE1A); /* enable output-compare int. */
  TCNT1 = 0; /* reset counter */
	
  SREG=sreg;
}

void check_keys(void)
{
  uint8_t sreg;
  uint8_t updaterequired, minute_update;
  static uint8_t tlastchange = 0;
	
  /* 
     if no key is pressed "long" and the last update with
     "shortly" pressed key has been done < DTCHECKKEY*5ms before
     "now" do nothing and return 
  */
  if ( ( gKeyPlusCnt < KEYCNTLONG ) && ( gKeyMinusCnt < KEYCNTLONG ) && (gKeySelectCnt < KEYCNTLONG)) {
    if ( (uint8_t)(gHeartBeat10ms - tlastchange) < DTCHECKKEY ) return;
  }
	
  sreg=SREG;
	
  updaterequired=0;
  minute_update=0;
	
  if (gKeySelectCnt > KEYCNTMIN)
    {
      cli();
      updaterequired=1;
    
      adjust++;
      if(adjust > 4)
	adjust=0;
    }

  /* increment time */
  if ( gKeyPlusCnt > KEYCNTMIN ) {
    cli();	/* disable interrupts to avoid side-effect with ISRs */
    updaterequired=1;
    if (adjust == ADJ_MMHH)
      {
	minute_update=1; /* minute  update */
	RTC_m++;
	if (RTC_m > 59)
	  {
	    RTC_m = 0;
	    RTC_h++;
	    if (RTC_h > 23)
	      RTC_h = 0;
	  }
      }
    else if (adjust == ADJ_DDMM)
      {
	date++;
	if (date > 31)
	  {
	    date = 1;
	    month++;
	  }
	if (month == 13)
	  month=1;
      }
    else if ( adjust == ADJ_Y)
      year++;
    else if ( adjust == ADJ_W)
      {
	week++;
	if (week > 7)
	  week=1;
      }
  }
  
  /* decrement time */
  if ( gKeyMinusCnt > KEYCNTMIN ) {
    cli();
    updaterequired=1;
    if (adjust == ADJ_MMHH)
      {
	minute_update=1;
	if (RTC_m==0) {
	  RTC_m=59;
	  if (RTC_h==0) RTC_h=23;
	  else RTC_h--;
	}
	else RTC_m--;
      }
    else if (adjust == ADJ_DDMM)
      {
	if (date == 1)
	  {
	    date=31;
	    if (month == 1)
	      month=12;
	    else
	      month--;
	  }
	else
	  date--;
      }
    else if (adjust == ADJ_Y)
      year--;
    else if (adjust == ADJ_W)
      {
	if (week == 1)
	  week=7;
	else
	  week--;
      }
  }
  
  if (updaterequired) {
    if (minute_update)
      RTC_s = 0; /* reset seconds */
    tlastchange=gHeartBeat10ms; /* save change time */
  }

  SREG=sreg;
}

/****LED-dot-matrix I/O Init ****/
void init_led8x8(void)
{
  DDRD = 0xFF;
  DDRB |= 0x3F;
  DDRC |= ((1<<PINC4)|(1<<PINC5));
  
  PORTB |= 0x3F; 
  PORTC |= ((1<<PINC4)|(1<<PINC5));
}


void disp_led8x8()
{
  /* display date and time */
  /*
    todo:
    1)make it easier to read.
    2)display other character.
   */

  PORTD = (RTC_h/10)|((year/1000)<<4);
  PORTB &= ~0x01;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;

  PORTD = (RTC_h%10)|(((year%1000)/100)<<4);
  PORTB &= ~0x02;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;

  PORTD = (RTC_m/10)|((((year%1000)%100)/10)<<4);
  PORTB &= ~0x04;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;

  PORTD = (RTC_m%10)|((((year%1000)%100)%10)<<4);
  PORTB &= ~0x08;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;
  
  PORTD = (RTC_s/10)|((month/10)<<4);
  PORTB &= ~0x10;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;

  PORTD = (RTC_s%10)|((month%10)<<4);
  PORTB &= ~0x20;
  _delay_ms(LIGHTED_TIME);
  PORTB |= 0x3F;

  PORTD = (0x00)|((date/10)<<4);
  PORTC &= ~(1<<PINC4);
  _delay_ms(LIGHTED_TIME);
  PORTC |= (1<<PINC4);

  PORTD = (week)|((date%10)<<4);
  PORTC &= ~(1<<PINC5);
  _delay_ms(LIGHTED_TIME);
  PORTC |= (1<<PINC5);
}

int main(void)
{
  init_keys();
  
  RTC_init();
  
  init_led8x8();
  
  /* set initial date and time */
  RTC_set(22,6,33,2005,12,25,7);
  
  /* main loop */
  for (;;)                   
    {
      if(adjust == ADJ_DISP_OFF)
	_delay_ms(16);	
      else
	disp_led8x8();
      
      check_keys();
    }
	
  return 0; /* never reached */
}

Makefile


# Hey Emacs, this is a -*- makefile -*-
#
# WinAVR Sample makefile written by Eric B. Weddington, Jörg Wunsch, et al.
# Released to the Public Domain
# Please read the make user manual!
#
# Additional material for this makefile was submitted by:
#  Tim Henigan
#  Peter Fleury
#  Reiner Patommel
#  Sander Pool
#  Frederik Rouleau
#  Markus Pfaff
#
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make coff = Convert ELF to AVR COFF (for use with AVR Studio 3.x or VMLAB).
#
# make extcoff = Convert ELF to AVR Extended COFF (for use with AVR Studio
#                4.07 or greater).
#
# make program = Download the hex file to the device, using avrdude.  Please
#                customize the avrdude settings below first!
#
# make filename.s = Just compile filename.c into the assembler code only
#
# To rebuild project do "make clean" then "make all".
#


# MCU name
MCU = atmega48

# Output format. (can be srec, ihex, binary)
FORMAT = ihex

# Target file name (without extension).
TARGET = binclock


# List C source files here. (C dependencies are automatically generated.)
SRC = $(TARGET).c
#SRC += lcd.c lcdbignum.c rtcasync.c
SRC += rtcasync.c
# List Assembler source files here.
# Make them always end in a capital .S.  Files ending in a lowercase .s
# will not be considered source files but generated files (assembler
# output from the compiler), and will be deleted upon "make clean"!
# Even though the DOS/Win* filesystem matches both .s and .S the same,
# it will preserve the spelling of the filenames, and gcc itself does
# care about how the name is spelled on its command-line.
ASRC = 



# Optimization level, can be [0, 1, 2, 3, s]. 
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s

# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs

# List any extra directories to look for include files here.
#     Each directory must be seperated by a space.
EXTRAINCDIRS = 


# Compiler flag to set the C Standard level.
# c89   - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99   - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = -std=gnu99

# Place -D or -U options here
CDEFS =

# Place -I options here
CINCS =


# Compiler flags.
#  -g*:          generate debugging information
#  -O*:          optimization level
#  -f...:        tuning, see GCC manual and avr-libc documentation
#  -Wall...:     warning level
#  -Wa,...:      tell GCC to pass this to the assembler.
#    -adhlns...: create assembler listing
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes
CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)



# Assembler flags.
#  -Wa,...:   tell GCC to pass this to the assembler.
#  -ahlms:    create listing
#  -gstabs:   have the assembler create line number information; note that
#             for use in COFF files, additional information about filenames
#             and function names needs to be present in the assembler source
#             files -- see avr-libc docs [FIXME: not yet described there]
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 



#Additional libraries.

# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min

# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt

PRINTF_LIB = 

# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min

# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt

SCANF_LIB = 

MATH_LIB = -lm

# External memory options

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff

EXTMEMOPTS =

# Linker flags.
#  -Wl,...:     tell GCC to pass this to linker.
#    -Map:      create map file
#    --cref:    add cross reference to  map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)




# Programming support using avrdude. Settings and variables.

# Programming hardware: alf avr910 avrisp bascom bsd 
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
#
# Type: avrdude -c ?
# to get a full listing.
#
#AVRDUDE_PROGRAMMER = stk500
AVRDUDE_PROGRAMMER = stk500v2

# com1 = serial port. Use lpt1 to connect to parallel port.
AVRDUDE_PORT = /dev/ttyUSB0    # programmer connected to serial device

AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep


# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y

# Uncomment the following if you do /not/ wish a verification to be
# performed after programming the device.
#AVRDUDE_NO_VERIFY = -V

# Increase verbosity level.  Please use this when submitting bug
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude> 
# to submit bug reports.
#AVRDUDE_VERBOSE = -v -v

AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)



# ---------------------------------------------------------------------------

# Define directories, if needed.
DIRAVR = c:/winavr
DIRAVRBIN = $(DIRAVR)/bin
DIRAVRUTILS = $(DIRAVR)/utils/bin
DIRINC = .
DIRLIB = $(DIRAVR)/avr/lib


# Define programs and commands.
SHELL = sh
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
COPY = cp




# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = --------  end  --------
MSG_SIZE_BEFORE = Size before: 
MSG_SIZE_AFTER = Size after:
MSG_COFF = Converting to AVR COFF:
MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:




# Define all object files.
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) 

# Define all listing files.
LST = $(ASRC:.S=.lst) $(SRC:.c=.lst)


# Compiler flags to generate dependency files.
GENDEPFLAGS = -Wp,-M,-MP,-MT,$(*F).o,-MF,.dep/$(@F).d


# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)





# Default target.
all: begin gccversion sizebefore build sizeafter finished end

build: elf hex eep lss sym

elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
lss: $(TARGET).lss 
sym: $(TARGET).sym



# Eye candy.
# AVR Studio 3.x does not check make's exit code but relies on
# the following magic strings to be generated by the compile job.
begin:
	@echo
	@echo $(MSG_BEGIN)

finished:
	@echo $(MSG_ERRORS_NONE)

end:
	@echo $(MSG_END)
	@echo


# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
ELFSIZE = $(SIZE) -A $(TARGET).elf
sizebefore:
	@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi

sizeafter:
	@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi



# Display compiler version information.
gccversion : 
	@$(CC) --version



# Program the device.  
program: $(TARGET).hex $(TARGET).eep
	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)




# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000 


coff: $(TARGET).elf
	@echo
	@echo $(MSG_COFF) $(TARGET).cof
	$(COFFCONVERT) -O coff-avr $< $(TARGET).cof


extcoff: $(TARGET).elf
	@echo
	@echo $(MSG_EXTENDED_COFF) $(TARGET).cof
	$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof



# Create final output files (.hex, .eep) from ELF output file.
%.hex: %.elf
	@echo
	@echo $(MSG_FLASH) $@
	$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@

%.eep: %.elf
	@echo
	@echo $(MSG_EEPROM) $@
	-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
	--change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Create extended listing file from ELF output file.
%.lss: %.elf
	@echo
	@echo $(MSG_EXTENDED_LISTING) $@
	$(OBJDUMP) -h -S $< > $@

# Create a symbol table from ELF output file.
%.sym: %.elf
	@echo
	@echo $(MSG_SYMBOL_TABLE) $@
	$(NM) -n $< > $@



# Link: create ELF output file from object files.
.SECONDARY : $(TARGET).elf
.PRECIOUS : $(OBJ)
%.elf: $(OBJ)
	@echo
	@echo $(MSG_LINKING) $@
	$(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS)


# Compile: create object files from C source files.
%.o : %.c
	@echo
	@echo $(MSG_COMPILING) $<
	$(CC) -c $(ALL_CFLAGS) $< -o $@ 


# Compile: create assembler files from C source files.
%.s : %.c
	$(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
%.o : %.S
	@echo
	@echo $(MSG_ASSEMBLING) $<
	$(CC) -c $(ALL_ASFLAGS) $< -o $@



# Target: clean project.
clean: begin clean_list finished end

clean_list :
	@echo
	@echo $(MSG_CLEANING)
	$(REMOVE) $(TARGET).hex
	$(REMOVE) $(TARGET).eep
	$(REMOVE) $(TARGET).obj
	$(REMOVE) $(TARGET).cof
	$(REMOVE) $(TARGET).elf
	$(REMOVE) $(TARGET).map
	$(REMOVE) $(TARGET).obj
	$(REMOVE) $(TARGET).a90
	$(REMOVE) $(TARGET).sym
	$(REMOVE) $(TARGET).lnk
	$(REMOVE) $(TARGET).lss
	$(REMOVE) $(OBJ)
	$(REMOVE) $(LST)
	$(REMOVE) $(SRC:.c=.s)
	$(REMOVE) $(SRC:.c=.d)
	$(REMOVE) .dep/*



# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)


# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex eep lss sym coff extcoff \
clean clean_list program



sun ge 2007-10-24