AVR Libc Home Page | AVR Libc Development Pages | ||||
Main Page | User Manual | Library Reference | FAQ | Alphabetical Index | Example Projects |
Makefile
can be configured.PWM
) to ramp an LED on and off every two seconds. An AT90S2313 processor will be used as the controller. The circuit for this demonstration is shown in the schematic diagram. If you have a development kit, you should be able to use it, rather than build the circuit, for this project.
Schematic of circuit for demo project
The source code is given in demo.c. For the sake of this example, create a file called demo.c
containing this source code. Some of the more important parts of the code are:
iocompat.h
tries to abstract between all this differences using some preprocessor #ifdef
statements, so the actual program itself can operate on a common set of symbolic names. The macros defined by that file are:OCR
the name of the OCR register used to control the PWM (usually either OCR1 or OCR1A)DDROC
the name of the DDR (data direction register) for the OC outputOC1
the pin number of the OC1[A] output within its portTIMER1_TOP
the TOP value of the timer used for the PWM (1023 for 10-bit PWMs, 255 for devices that can only handle an 8-bit PWM)TIMER1_PWM_INIT
the initialization bits to be set into control register 1A in order to setup 10-bit (or 8-bit) phase and frequency correct PWM modeTIMER1_CLOCKSOURCE
the clock bits to set in the respective control register to start the PWM timer; usually the timer runs at full CPU clock for 10-bit PWMs, while it runs on a prescaled clock for 8-bit PWMs
PWM
is being used in 10-bit mode, so we need a 16-bit variable to remember the current value.PWM
.PWM
register. Since we are in an interrupt routine, it is safe to use a 16-bit assignment to the register. Outside of an interrupt, the assignment should only be performed with interrupts disabled if there's a chance that an interrupt routine could also access this register (or another register that uses TEMP
), see the appropriate FAQ entry.PWM
and enables interrupts.sleep_mode()
puts the processor on sleep until the next interrupt, to conserve power. Of course, that probably won't be noticable as we are still driving a LED, it is merely mentioned here to demonstrate the basic principle./* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch * ---------------------------------------------------------------------------- * * Simple AVR demonstration. Controls a LED that can be directly * connected from OC1/OC1A to GND. The brightness of the LED is * controlled with the PWM. After each period of the PWM, the PWM * value is either incremented or decremented, that's all. * * $Id: demo.c,v 1.9 2006/01/05 21:30:10 joerg_wunsch Exp $ */ #include <inttypes.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #include "iocompat.h" /* Note [1] */ enum { UP, DOWN }; ISR (TIMER1_OVF_vect) /* Note [2] */ { static uint16_t pwm; /* Note [3] */ static uint8_t direction; switch (direction) /* Note [4] */ { case UP: if (++pwm == TIMER1_TOP) direction = DOWN; break; case DOWN: if (--pwm == 0) direction = UP; break; } OCR = pwm; /* Note [5] */ } void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; /* * Start timer 1. * * NB: TCCR1A and TCCR1B could actually be the same register, so * take care to not clobber it. */ TCCR1B |= TIMER1_CLOCKSOURCE; /* * Run any device-dependent timer 1 setup hook if present. */ #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* Set PWM value to 0. */ OCR = 0; /* Enable OC1 as output. */ DDROC = _BV (OC1); /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); sei (); } int main (void) { ioinit (); /* loop forever, the interrupts are doing the rest */ for (;;) /* Note [7] */ sleep_mode(); return (0); }
-mmcu
option is specified. The -Os
option will tell the compiler to optimize the code for efficient space usage (at the possible expense of code execution speed). The -g
is used to embed debug info. The debug info is useful for disassemblies and doesn't end up in the
.hex files, so I usually specify it. Finally, the -c
tells the compiler to compile and stop -- don't link. This demo is small enough that we could compile and link in one step. However, real-world projects will have several modules and will typically need to break up the building of the project into several compiles and one link.
$ avr-gcc -g -Os -mmcu=atmega8 -c demo.c
The compilation will create a demo.o
file. Next we link it into a binary called demo.elf
.
$ avr-gcc -g -mmcu=atmega8 -o demo.elf demo.o
It is important to specify the MCU type when linking. The compiler uses the -mmcu
option to choose start-up files and run-time libraries that get linked together. If this option isn't specified, the compiler defaults to the 8515 processor environment, which is most certainly what you didn't want.
Now we have a binary file. Can we do anything useful with it (besides put it into the processor?) The GNU Binutils suite is made up of many useful tools for manipulating object files that get generated. One tool is avr-objdump
, which takes information from the object file and displays it in many useful ways. Typing the command by itself will cause it to list out its options.
For instance, to get a feel of the application's size, the -h
option can be used. The output of this option shows how much space is used in each of the sections (the .stab and
.stabstr sections hold the debugging information and won't make it into the ROM file).
An even more useful option is -S
. This option disassembles the binary file and intersperses the source code in the output! This method is much better, in my opinion, than using the -S
with the compiler because this listing includes routines from the libraries and the vector table contents. Also, all the "fix-ups" have been satisfied. In other words, the listing generated by this option reflects the actual code that the processor will run.
$ avr-objdump -h -S demo.elf > demo.lst
Here's the output as saved in the demo.lst
file:
demo.elf: file format elf32-avr Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000126 00000000 00000000 00000074 2**1 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .bss 00000003 00800060 00800060 0000019a 2**0 ALLOC 2 .debug_aranges 00000020 00000000 00000000 0000019a 2**0 CONTENTS, READONLY, DEBUGGING 3 .debug_pubnames 00000035 00000000 00000000 000001ba 2**0 CONTENTS, READONLY, DEBUGGING 4 .debug_info 00000105 00000000 00000000 000001ef 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_abbrev 000000cf 00000000 00000000 000002f4 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_line 0000014d 00000000 00000000 000003c3 2**0 CONTENTS, READONLY, DEBUGGING 7 .debug_frame 00000040 00000000 00000000 00000510 2**2 CONTENTS, READONLY, DEBUGGING 8 .debug_str 000000b1 00000000 00000000 00000550 2**0 CONTENTS, READONLY, DEBUGGING Disassembly of section .text: 00000000 <__vectors>: 0: 12 c0 rjmp .+36 ; 0x26 <__ctors_end> 2: 8c c0 rjmp .+280 ; 0x11c <__bad_interrupt> 4: 8b c0 rjmp .+278 ; 0x11c <__bad_interrupt> 6: 8a c0 rjmp .+276 ; 0x11c <__bad_interrupt> 8: 89 c0 rjmp .+274 ; 0x11c <__bad_interrupt> a: 88 c0 rjmp .+272 ; 0x11c <__bad_interrupt> c: 87 c0 rjmp .+270 ; 0x11c <__bad_interrupt> e: 86 c0 rjmp .+268 ; 0x11c <__bad_interrupt> 10: 25 c0 rjmp .+74 ; 0x5c <__vector_8> 12: 84 c0 rjmp .+264 ; 0x11c <__bad_interrupt> 14: 83 c0 rjmp .+262 ; 0x11c <__bad_interrupt> 16: 82 c0 rjmp .+260 ; 0x11c <__bad_interrupt> 18: 81 c0 rjmp .+258 ; 0x11c <__bad_interrupt> 1a: 80 c0 rjmp .+256 ; 0x11c <__bad_interrupt> 1c: 7f c0 rjmp .+254 ; 0x11c <__bad_interrupt> 1e: 7e c0 rjmp .+252 ; 0x11c <__bad_interrupt> 20: 7d c0 rjmp .+250 ; 0x11c <__bad_interrupt> 22: 7c c0 rjmp .+248 ; 0x11c <__bad_interrupt> 24: 7b c0 rjmp .+246 ; 0x11c <__bad_interrupt> 00000026 <__ctors_end>: 26: 11 24 eor r1, r1 28: 1f be out 0x3f, r1 ; 63 2a: cf e5 ldi r28, 0x5F ; 95 2c: d4 e0 ldi r29, 0x04 ; 4 2e: de bf out 0x3e, r29 ; 62 30: cd bf out 0x3d, r28 ; 61 00000032 <__do_copy_data>: 32: 10 e0 ldi r17, 0x00 ; 0 34: a0 e6 ldi r26, 0x60 ; 96 36: b0 e0 ldi r27, 0x00 ; 0 38: e6 e2 ldi r30, 0x26 ; 38 3a: f1 e0 ldi r31, 0x01 ; 1 3c: 02 c0 rjmp .+4 ; 0x42 <.do_copy_data_start> 0000003e <.do_copy_data_loop>: 3e: 05 90 lpm r0, Z+ 40: 0d 92 st X+, r0 00000042 <.do_copy_data_start>: 42: a0 36 cpi r26, 0x60 ; 96 44: b1 07 cpc r27, r17 46: d9 f7 brne .-10 ; 0x3e <.do_copy_data_loop> 00000048 <__do_clear_bss>: 48: 10 e0 ldi r17, 0x00 ; 0 4a: a0 e6 ldi r26, 0x60 ; 96 4c: b0 e0 ldi r27, 0x00 ; 0 4e: 01 c0 rjmp .+2 ; 0x52 <.do_clear_bss_start> 00000050 <.do_clear_bss_loop>: 50: 1d 92 st X+, r1 00000052 <.do_clear_bss_start>: 52: a3 36 cpi r26, 0x63 ; 99 54: b1 07 cpc r27, r17 56: e1 f7 brne .-8 ; 0x50 <.do_clear_bss_loop> 58: 4d d0 rcall .+154 ; 0xf4 <main> 5a: 61 c0 rjmp .+194 ; 0x11e <exit> 0000005c <__vector_8>: #include "iocompat.h" /* Note [1] */ enum { UP, DOWN }; ISR (TIMER1_OVF_vect) /* Note [2] */ { 5c: 1f 92 push r1 5e: 0f 92 push r0 60: 0f b6 in r0, 0x3f ; 63 62: 0f 92 push r0 64: 11 24 eor r1, r1 66: 2f 93 push r18 68: 3f 93 push r19 6a: 8f 93 push r24 static uint16_t pwm; /* Note [3] */ static uint8_t direction; switch (direction) /* Note [4] */ 6c: 80 91 60 00 lds r24, 0x0060 70: 88 23 and r24, r24 72: c1 f4 brne .+48 ; 0xa4 <__vector_8+0x48> { case UP: if (++pwm == TIMER1_TOP) 74: 20 91 61 00 lds r18, 0x0061 78: 30 91 62 00 lds r19, 0x0062 7c: 2f 5f subi r18, 0xFF ; 255 7e: 3f 4f sbci r19, 0xFF ; 255 80: 30 93 62 00 sts 0x0062, r19 84: 20 93 61 00 sts 0x0061, r18 88: 83 e0 ldi r24, 0x03 ; 3 8a: 2f 3f cpi r18, 0xFF ; 255 8c: 38 07 cpc r19, r24 8e: 09 f1 breq .+66 ; 0xd2 <__vector_8+0x76> if (--pwm == 0) direction = UP; break; } OCR = pwm; /* Note [5] */ 90: 3b bd out 0x2b, r19 ; 43 92: 2a bd out 0x2a, r18 ; 42 } 94: 8f 91 pop r24 96: 3f 91 pop r19 98: 2f 91 pop r18 9a: 0f 90 pop r0 9c: 0f be out 0x3f, r0 ; 63 9e: 0f 90 pop r0 a0: 1f 90 pop r1 a2: 18 95 reti ISR (TIMER1_OVF_vect) /* Note [2] */ { static uint16_t pwm; /* Note [3] */ static uint8_t direction; switch (direction) /* Note [4] */ a4: 81 30 cpi r24, 0x01 ; 1 a6: 29 f0 breq .+10 ; 0xb2 <__vector_8+0x56> a8: 20 91 61 00 lds r18, 0x0061 ac: 30 91 62 00 lds r19, 0x0062 b0: ef cf rjmp .-34 ; 0x90 <__vector_8+0x34> if (++pwm == TIMER1_TOP) direction = DOWN; break; case DOWN: if (--pwm == 0) b2: 20 91 61 00 lds r18, 0x0061 b6: 30 91 62 00 lds r19, 0x0062 ba: 21 50 subi r18, 0x01 ; 1 bc: 30 40 sbci r19, 0x00 ; 0 be: 30 93 62 00 sts 0x0062, r19 c2: 20 93 61 00 sts 0x0061, r18 c6: 21 15 cp r18, r1 c8: 31 05 cpc r19, r1 ca: 11 f7 brne .-60 ; 0x90 <__vector_8+0x34> direction = UP; cc: 10 92 60 00 sts 0x0060, r1 d0: df cf rjmp .-66 ; 0x90 <__vector_8+0x34> switch (direction) /* Note [4] */ { case UP: if (++pwm == TIMER1_TOP) direction = DOWN; d2: 81 e0 ldi r24, 0x01 ; 1 d4: 80 93 60 00 sts 0x0060, r24 d8: db cf rjmp .-74 ; 0x90 <__vector_8+0x34> 000000da <ioinit>: void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; da: 83 e8 ldi r24, 0x83 ; 131 dc: 8f bd out 0x2f, r24 ; 47 * Start timer 1. * * NB: TCCR1A and TCCR1B could actually be the same register, so * take care to not clobber it. */ TCCR1B |= TIMER1_CLOCKSOURCE; de: 8e b5 in r24, 0x2e ; 46 e0: 81 60 ori r24, 0x01 ; 1 e2: 8e bd out 0x2e, r24 ; 46 #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* Set PWM value to 0. */ OCR = 0; e4: 1b bc out 0x2b, r1 ; 43 e6: 1a bc out 0x2a, r1 ; 42 /* Enable OC1 as output. */ DDROC = _BV (OC1); e8: 82 e0 ldi r24, 0x02 ; 2 ea: 87 bb out 0x17, r24 ; 23 /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); ec: 84 e0 ldi r24, 0x04 ; 4 ee: 89 bf out 0x39, r24 ; 57 sei (); f0: 78 94 sei } f2: 08 95 ret 000000f4 <main>: void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; f4: 83 e8 ldi r24, 0x83 ; 131 f6: 8f bd out 0x2f, r24 ; 47 * Start timer 1. * * NB: TCCR1A and TCCR1B could actually be the same register, so * take care to not clobber it. */ TCCR1B |= TIMER1_CLOCKSOURCE; f8: 8e b5 in r24, 0x2e ; 46 fa: 81 60 ori r24, 0x01 ; 1 fc: 8e bd out 0x2e, r24 ; 46 #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* Set PWM value to 0. */ OCR = 0; fe: 1b bc out 0x2b, r1 ; 43 100: 1a bc out 0x2a, r1 ; 42 /* Enable OC1 as output. */ DDROC = _BV (OC1); 102: 82 e0 ldi r24, 0x02 ; 2 104: 87 bb out 0x17, r24 ; 23 /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); 106: 84 e0 ldi r24, 0x04 ; 4 108: 89 bf out 0x39, r24 ; 57 sei (); 10a: 78 94 sei ioinit (); /* loop forever, the interrupts are doing the rest */ for (;;) /* Note [7] */ sleep_mode(); 10c: 85 b7 in r24, 0x35 ; 53 10e: 80 68 ori r24, 0x80 ; 128 110: 85 bf out 0x35, r24 ; 53 112: 88 95 sleep 114: 85 b7 in r24, 0x35 ; 53 116: 8f 77 andi r24, 0x7F ; 127 118: 85 bf out 0x35, r24 ; 53 11a: f8 cf rjmp .-16 ; 0x10c <main+0x18> 0000011c <__bad_interrupt>: 11c: 71 cf rjmp .-286 ; 0x0 <__vectors> 0000011e <exit>: 11e: f8 94 cli 120: 00 c0 rjmp .+0 ; 0x122 <_exit> 00000122 <_exit>: 122: f8 94 cli 00000124 <__stop_program>: 124: ff cf rjmp .-2 ; 0x124 <__stop_program>
avr-objdump
is very useful, but sometimes it's necessary to see information about the link that can only be generated by the linker. A map file contains this information. A map file is useful for monitoring the sizes of your code and data. It also shows where modules are loaded and which modules were loaded from libraries. It is yet another view of your application. To get a map file, I usually add -Wl,-Map,demo.map
to my link command. Relink the application using the following command to generate demo.map
(a portion of which is shown below).
$ avr-gcc -g -mmcu=atmega8 -Wl,-Map,demo.map -o demo.elf demo.o
Some points of interest in the demo.map
file are:
.rela.plt *(.rela.plt) .text 0x00000000 0x126 *(.vectors) .vectors 0x00000000 0x26 c:/avrdev/avr-libc/avr-libc-1.6.4/avr/lib/avr4/atmega8/crtm8.o 0x00000000 __vectors 0x00000000 __vector_default *(.vectors) *(.progmem.gcc*) *(.progmem*) 0x00000026 . = ALIGN (0x2) 0x00000026 __trampolines_start = . *(.trampolines) .trampolines 0x00000026 0x0 linker stubs *(.trampolines*) 0x00000026 __trampolines_end = . *(.jumptables) *(.jumptables*) *(.lowtext) *(.lowtext*) 0x00000026 __ctors_start = .
The .text segment (where program instructions are stored) starts at location 0x0.
*(.fini2) *(.fini2) *(.fini1) *(.fini1) *(.fini0) .fini0 0x00000122 0x4 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_exit.o) *(.fini0) 0x00000126 _etext = . .data 0x00800060 0x0 load address 0x00000126 0x00800060 PROVIDE (__data_start, .) *(.data) .data 0x00800060 0x0 demo.o .data 0x00800060 0x0 c:/avrdev/avr-libc/avr-libc-1.6.4/avr/lib/avr4/atmega8/crtm8.o .data 0x00800060 0x0 c:/avrdev/avr-libc/avr-libc-1.6.4/avr/lib/avr4/exit.o .data 0x00800060 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_exit.o) .data 0x00800060 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_copy_data.o) .data 0x00800060 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_clear_bss.o) *(.data*) *(.rodata) *(.rodata*) *(.gnu.linkonce.d*) 0x00800060 . = ALIGN (0x2) 0x00800060 _edata = . 0x00800060 PROVIDE (__data_end, .) .bss 0x00800060 0x3 0x00800060 PROVIDE (__bss_start, .) *(.bss) .bss 0x00800060 0x3 demo.o .bss 0x00800063 0x0 c:/avrdev/avr-libc/avr-libc-1.6.4/avr/lib/avr4/atmega8/crtm8.o .bss 0x00800063 0x0 c:/avrdev/avr-libc/avr-libc-1.6.4/avr/lib/avr4/exit.o .bss 0x00800063 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_exit.o) .bss 0x00800063 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_copy_data.o) .bss 0x00800063 0x0 c:/winavr/bin/../lib/gcc/avr/4.3.2/avr4\libgcc.a(_clear_bss.o) *(.bss*) *(COMMON) 0x00800063 PROVIDE (__bss_end, .) 0x00000126 __data_load_start = LOADADDR (.data) 0x00000126 __data_load_end = (__data_load_start + SIZEOF (.data)) .noinit 0x00800063 0x0 0x00800063 PROVIDE (__noinit_start, .) *(.noinit*) 0x00800063 PROVIDE (__noinit_end, .) 0x00800063 _end = . 0x00800063 PROVIDE (__heap_start, .) .eeprom 0x00810000 0x0 *(.eeprom*) 0x00810000 __eeprom_end = .
The last address in the .text segment is location
0x114
( denoted by _etext
), so the instructions use up 276 bytes of FLASH.
The .data segment (where initialized static variables are stored) starts at location
0x60
, which is the first address after the register bank on an ATmega8 processor.
The next available address in the .data segment is also location
0x60
, so the application has no initialized data.
The .bss segment (where uninitialized data is stored) starts at location
0x60
.
The next available address in the .bss segment is location 0x63, so the application uses 3 bytes of uninitialized data.
The .eeprom segment (where EEPROM variables are stored) starts at location 0x0.
The next available address in the .eeprom segment is also location 0x0, so there aren't any EEPROM variables.
.hex files. The GNU utility that does this is called avr-objcopy
.The ROM contents can be pulled from our project's binary and put into the file demo.hex using the following command:
$ avr-objcopy -j .text -j .data -O ihex demo.elf demo.hex
The resulting demo.hex
file contains:
:1000000012C08CC08BC08AC089C088C087C086C01F :1000100025C084C083C082C081C080C07FC07EC034 :100020007DC07CC07BC011241FBECFE5D4E0DEBF05 :10003000CDBF10E0A0E6B0E0E6E2F1E002C005903E :100040000D92A036B107D9F710E0A0E6B0E001C0EC :100050001D92A336B107E1F74DD061C01F920F92F8 :100060000FB60F9211242F933F938F9380916000CE :100070008823C1F420916100309162002F5F3F4FCF :10008000309362002093610083E02F3F380709F12D :100090003BBD2ABD8F913F912F910F900FBE0F90C6 :1000A0001F901895813029F02091610030916200F5 :1000B000EFCF2091610030916200215030403093A9 :1000C0006200209361002115310511F71092600044 :1000D000DFCF81E080936000DBCF83E88FBD8EB5FA :1000E00081608EBD1BBC1ABC82E087BB84E089BFE7 :1000F0007894089583E88FBD8EB581608EBD1BBC5A :100100001ABC82E087BB84E089BF789485B7806899 :1001100085BF889585B78F7785BFF8CF71CFF89465 :0601200000C0F894FFCFBF :00000001FF
The -j
option indicates that we want the information from the .text and
.data segment extracted. If we specify the EEPROM segment, we can generate a
.hex file that can be used to program the EEPROM:
$ avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex demo.elf demo_eeprom.hex
There is no demo_eeprom.hex
file written, as that file would be empty.
Starting with version 2.17 of the GNU binutils, the avr-objcopy
command that used to generate the empty EEPROM files now aborts because of the empty input section .eeprom, so these empty files are not generated. It also signals an error to the Makefile which will be caught there, and makes it print a message about the empty file not being generated.
make
, save the following in a file called Makefile
.
Makefile
can only be used as input for the GNU version of make
.PRG = demo OBJ = demo.o #MCU_TARGET = at90s2313 #MCU_TARGET = at90s2333 #MCU_TARGET = at90s4414 #MCU_TARGET = at90s4433 #MCU_TARGET = at90s4434 #MCU_TARGET = at90s8515 #MCU_TARGET = at90s8535 #MCU_TARGET = atmega128 #MCU_TARGET = atmega1280 #MCU_TARGET = atmega1281 #MCU_TARGET = atmega1284p #MCU_TARGET = atmega16 #MCU_TARGET = atmega163 #MCU_TARGET = atmega164p #MCU_TARGET = atmega165 #MCU_TARGET = atmega165p #MCU_TARGET = atmega168 #MCU_TARGET = atmega169 #MCU_TARGET = atmega169p #MCU_TARGET = atmega2560 #MCU_TARGET = atmega2561 #MCU_TARGET = atmega32 #MCU_TARGET = atmega324p #MCU_TARGET = atmega325 #MCU_TARGET = atmega3250 #MCU_TARGET = atmega329 #MCU_TARGET = atmega3290 #MCU_TARGET = atmega48 #MCU_TARGET = atmega64 #MCU_TARGET = atmega640 #MCU_TARGET = atmega644 #MCU_TARGET = atmega644p #MCU_TARGET = atmega645 #MCU_TARGET = atmega6450 #MCU_TARGET = atmega649 #MCU_TARGET = atmega6490 MCU_TARGET = atmega8 #MCU_TARGET = atmega8515 #MCU_TARGET = atmega8535 #MCU_TARGET = atmega88 #MCU_TARGET = attiny2313 #MCU_TARGET = attiny24 #MCU_TARGET = attiny25 #MCU_TARGET = attiny26 #MCU_TARGET = attiny261 #MCU_TARGET = attiny44 #MCU_TARGET = attiny45 #MCU_TARGET = attiny461 #MCU_TARGET = attiny84 #MCU_TARGET = attiny85 #MCU_TARGET = attiny861 OPTIMIZE = -O2 DEFS = LIBS = # You should not have to change anything below here. CC = avr-gcc # Override is only needed by avr-lib build system. override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) override LDFLAGS = -Wl,-Map,$(PRG).map OBJCOPY = avr-objcopy OBJDUMP = avr-objdump all: $(PRG).elf lst text eeprom $(PRG).elf: $(OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) # dependency: demo.o: demo.c iocompat.h clean: rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak rm -rf *.lst *.map $(EXTRA_CLEAN_FILES) lst: $(PRG).lst %.lst: %.elf $(OBJDUMP) -h -S $< > $@ # Rules for building the .text rom images text: hex bin srec hex: $(PRG).hex bin: $(PRG).bin srec: $(PRG).srec %.hex: %.elf $(OBJCOPY) -j .text -j .data -O ihex $< $@ %.srec: %.elf $(OBJCOPY) -j .text -j .data -O srec $< $@ %.bin: %.elf $(OBJCOPY) -j .text -j .data -O binary $< $@ # Rules for building the .eeprom rom images eeprom: ehex ebin esrec ehex: $(PRG)_eeprom.hex ebin: $(PRG)_eeprom.bin esrec: $(PRG)_eeprom.srec %_eeprom.hex: %.elf $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \ || { echo empty $@ not generated; exit 0; } %_eeprom.srec: %.elf $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \ || { echo empty $@ not generated; exit 0; } %_eeprom.bin: %.elf $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \ || { echo empty $@ not generated; exit 0; } # Every thing below here is used by avr-libc's build system and can be ignored # by the casual user. FIG2DEV = fig2dev EXTRA_CLEAN_FILES = *.hex *.bin *.srec dox: eps png pdf eps: $(PRG).eps png: $(PRG).png pdf: $(PRG).pdf %.eps: %.fig $(FIG2DEV) -L eps $< $@ %.pdf: %.fig $(FIG2DEV) -L pdf $< $@ %.png: %.fig $(FIG2DEV) -L png $< $@