AVR Libc Home Page | ![]() |
AVR Libc Development Pages | ||
Main Page | FAQ | Library Reference | Additional Documentation | Example Projects |
この時点であなたは既にGNUツールのインストールを終えていることでしょう。この章では、我々はあなたにAVRプロジェクトのGNUツールを使って小さな製作例をお見せします。この章を読み終わる頃にはあなたはこれらのツールがどのように使われ、Makefileをどのように作成するか感じが掴めるようになるでしょう。
このプロジェクトでは、パルス幅変調(PWM)を使ってLEDを2秒ごとに点滅させます。AT90S2313プロセッサを使います。この試作回路を schematic diagramに示します。 もしあなたが開発キットをお持ちなら、自作するよりはそれを使った方がよいでしょう。
Schematic of circuit for demo project
ソースコードは demo.c です。 この例のために、 demo.c
というファイルが作られます。コードの勘所としては:
iocompat.h
は、#ifdef節を使っていくつかのデバイスに対しこれらの違いを吸収しようと試みたものです。実際のプログラムは、シンボル名共通セット定義の上で動きます。このファイルで定義されているマクロは以下のものがあります。
OCR
PWMコントロールに使われるOCRレジスタの名前 (通常OCR1又はOCR1A)DDROC
OC(比較一致出力)ピンを含むポートに対応するDDRレジスタOC1
OC1[A] 比較一致出力ピンのピン番号(PORTレジスタ内のビット番号)TIMER1_TOP
PWMに使われるタイマのTOP値。10-bit PWMなら1023、8-bit
PWMしか対応できないデバイスなら255TIMER1_PWM_INIT
タイマ1コントロールレジスタ1Aの初期化ビット情報。10
or 8-bit 位相周波数PWM設定TIMER1_CLOCKSOURCE
PWMタイマをスタートさせるためのクロック設定ビット。通常10-bit
PWMについてはプレスケーラなし、8-bit PWMについてはプリスケーラ付きクロックソースで動作させます。sleep_mode()
で次の割り込みまでプロセッサをスリープにしています。もちろん、この電力削減はLED点灯による電力消費の前には微々たるものではあります。ここではあくまで基礎的な原則のデモンストレーションのために行ってみました。
/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <joerg@FreeBSD.ORG> がこのファイルを書きました。あなたがこのことを覚えている限り、 * この作品を好きなように使って構いません。いつの日かお会いする機会があり、この作品が * あなたにとって価値があったと思ったなら、お返しにビールを一杯奢ってください! * Joerg Wunsch * ---------------------------------------------------------------------------- * * 簡単なAVRのデモプログラムです。OC1/OC1AとGND間に直結したLEDをコントロールします。 * LEDの明るさはPWMによってコントロールされます。PWM1周期毎に、PWM値は増加又は * 減少します。以上。 * * $Id: demo.c,v 1.6.2.3 2006/01/05 21:33:08 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] */ { /* TMR1を10-bit PWMで動かす(一部のATtinyデバイスでは8-bit PWM) */ TCCR1A = TIMER1_PWM_INIT; /* * timer 1 をスタートする。 * * NB: TCCR1A と TCCR1B は同じレジスタを指すことがあり得ます。could actually be the same register, so * 衝突が起こらないよう注意してください。 */ TCCR1B |= TIMER1_CLOCKSOURCE; /* * もしあれば、デバイス依存タイマ1セットアップフックプログラムを呼び出す。 */ #if defined(TIMER1_SETUP_HOOK) TIMER1_SETUP_HOOK(); #endif /* PWM値を0に */ OCR = 0; /* OC1を出力設定に */ DDROC = _BV (OC1); /* タイマ1オーバーフロー割り込み有効 */ TIMSK = _BV (TOIE1); sei (); } int main (void) { ioinit (); /* 無限ループ。割り込みが残りを実行します。 */ for (;;) /* Note [7] */ sleep_mode(); return (0); }
最初に必要なことはソースをコンパイルすることです。コンパイル時、コンパイラはプロセッサのタイプを-mmcuオプションで知る必要があります。-Os オプションをつければコードをコンパイル後容量ができるだけ小さくなるよう最適化(速度をそれほど犠牲にしない範囲内で)することをコンパイラに指示できます。
-g オプションはデバッグに便利な情報を埋め込むのに使われます。デバッグ情報はディスアセンブルするときに便利で、最終的に.hex ファイルに埋め込まれてメモリを圧迫することはありませんので、私はいつもこのオプションを指定しています。
最後に、-c オプションはコンパイラにコンパイルだけを行って終了するように伝えています。リンクまでは行いません。このデモプログラムは小さく、コンパイルとリンクを1度に行うことができますが、現実のちゃんとした(もっと大きな)プロジェクトではいくつかのモジュールを必要とすることが多く、何度かのコンパイルと1度のリンクに分けられることが多いです。
$ avr-gcc -g -Os -mmcu=atmega8 -c demo.c
コンパイルは demo.o
ファイルを生成します。次に、demo.elfと呼ばれるバイナリを作るためにリンクしましょう。
$ avr-gcc -g -mmcu=atmega8 -o demo.elf demo.o
リンクの際、MCUのタイプを指定することは大切なことです。コンパイラは-mmcuオプションを、リンク時組み込むスタートアップファイルやランタイムライブラリを選択するために参照しています。このオプションが指定されない場合は、コンパイラは8515プロセッサと見なしてリンクを行いますが、これはあなたが望むことじゃないでしょう。
さて、バイナリファイルができました。他にやることがあるでしょうか(AVRマイコンチップに書き込みを行うこと以外)?
GNU Binutilsセットには、生成されたオブジェクトファイルを取り扱うのに有用なたくさんのツールがあります。その1つが avr-objdumpです。これはオブジェクトファイルから各種情報を取り出し、いろいろ有用な方法で情報を提示します。コマンド名だけをタイプすると、オプションのリストが表示されます。
簡単に紹介すると、できあがったアプリケーションのサイズを知りたいときは、-h optionが使えます。このオプションの出力は各 section がどれ位のメモリスペースを使っているかを表しています。(. .stab と.stabstr セクションはデバッグ情報なので、ROMファイルには書き込まれません。)
さらに有用なオプションが -S オプションです。このオプションはバイナリファイルを逆アセンブルし、その出力にソースコードを挟み込んでくれます!。私は、この方法はコンパイラで-Sオプションをつけるより有用だと思います。このリスティングにはライブラリ内のルーチンやベクタテーブルも含まれるからです。さらに、多くの改良がまた満足させてくれます??。言い換えれば、このオプションによるリスティングは実際にプロセッサが実行するコードを表示しているわけです。
$ avr-objdump -h -S demo.elf > demo.lst
上記により、demo.lst ファイルに出力が保存されます。
demo.elf: file format elf32-avr Sections: Idx Name Size VMA LMA File off Algn 0 .text 000000ec 00000000 00000000 00000094 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000000 00800060 000000ec 00000180 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000003 00800060 00800060 00000180 2**0 ALLOC 3 .noinit 00000000 00800063 00800063 00000180 2**0 CONTENTS 4 .eeprom 00000000 00810000 00810000 00000180 2**0 CONTENTS 5 .stab 0000078c 00000000 00000000 00000180 2**2 CONTENTS, READONLY, DEBUGGING 6 .stabstr 000007ca 00000000 00000000 0000090c 2**0 CONTENTS, READONLY, DEBUGGING Disassembly of section .text: 00000000 <__vectors>: 0: 12 c0 rjmp .+36 ; 0x26 <__ctors_end> 2: 73 c0 rjmp .+230 ; 0xea <__bad_interrupt> 4: 72 c0 rjmp .+228 ; 0xea <__bad_interrupt> 6: 71 c0 rjmp .+226 ; 0xea <__bad_interrupt> 8: 70 c0 rjmp .+224 ; 0xea <__bad_interrupt> a: 6f c0 rjmp .+222 ; 0xea <__bad_interrupt> c: 6e c0 rjmp .+220 ; 0xea <__bad_interrupt> e: 6d c0 rjmp .+218 ; 0xea <__bad_interrupt> 10: 11 c0 rjmp .+34 ; 0x34 <__vector_8> 12: 6b c0 rjmp .+214 ; 0xea <__bad_interrupt> 14: 6a c0 rjmp .+212 ; 0xea <__bad_interrupt> 16: 69 c0 rjmp .+210 ; 0xea <__bad_interrupt> 18: 68 c0 rjmp .+208 ; 0xea <__bad_interrupt> 1a: 67 c0 rjmp .+206 ; 0xea <__bad_interrupt> 1c: 66 c0 rjmp .+204 ; 0xea <__bad_interrupt> 1e: 65 c0 rjmp .+202 ; 0xea <__bad_interrupt> 20: 64 c0 rjmp .+200 ; 0xea <__bad_interrupt> 22: 63 c0 rjmp .+198 ; 0xea <__bad_interrupt> 24: 62 c0 rjmp .+196 ; 0xea <__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 32: 4e c0 rjmp .+156 ; 0xd0 <main> 00000034 <__vector_8>: enum { UP, DOWN }; ISR (TIMER1_OVF_vect) /* Note [2] */ { 34: 1f 92 push r1 36: 0f 92 push r0 38: 0f b6 in r0, 0x3f ; 63 3a: 0f 92 push r0 3c: 11 24 eor r1, r1 3e: 2f 93 push r18 40: 3f 93 push r19 42: 8f 93 push r24 44: 9f 93 push r25 static uint16_t pwm; /* Note [3] */ static uint8_t direction; switch (direction) /* Note [4] */ 46: 80 91 62 00 lds r24, 0x0062 4a: 99 27 eor r25, r25 4c: 00 97 sbiw r24, 0x00 ; 0 4e: 39 f0 breq .+14 ; 0x5e <__SREG__+0x1f> 50: 01 97 sbiw r24, 0x01 ; 1 52: b9 f0 breq .+46 ; 0x82 <__SREG__+0x43> 54: 20 91 60 00 lds r18, 0x0060 58: 30 91 61 00 lds r19, 0x0061 5c: 21 c0 rjmp .+66 ; 0xa0 <__SREG__+0x61> { case UP: if (++pwm == TIMER1_TOP) 5e: 20 91 60 00 lds r18, 0x0060 62: 30 91 61 00 lds r19, 0x0061 66: 2f 5f subi r18, 0xFF ; 255 68: 3f 4f sbci r19, 0xFF ; 255 6a: 30 93 61 00 sts 0x0061, r19 6e: 20 93 60 00 sts 0x0060, r18 72: 83 e0 ldi r24, 0x03 ; 3 74: 2f 3f cpi r18, 0xFF ; 255 76: 38 07 cpc r19, r24 78: 99 f4 brne .+38 ; 0xa0 <__SREG__+0x61> direction = DOWN; 7a: 81 e0 ldi r24, 0x01 ; 1 7c: 80 93 62 00 sts 0x0062, r24 80: 0f c0 rjmp .+30 ; 0xa0 <__SREG__+0x61> break; case DOWN: if (--pwm == 0) 82: 20 91 60 00 lds r18, 0x0060 86: 30 91 61 00 lds r19, 0x0061 8a: 21 50 subi r18, 0x01 ; 1 8c: 30 40 sbci r19, 0x00 ; 0 8e: 30 93 61 00 sts 0x0061, r19 92: 20 93 60 00 sts 0x0060, r18 96: 21 15 cp r18, r1 98: 31 05 cpc r19, r1 9a: 11 f4 brne .+4 ; 0xa0 <__SREG__+0x61> direction = UP; 9c: 10 92 62 00 sts 0x0062, r1 break; } OCR = pwm; /* Note [5] */ a0: 3b bd out 0x2b, r19 ; 43 a2: 2a bd out 0x2a, r18 ; 42 a4: 9f 91 pop r25 a6: 8f 91 pop r24 a8: 3f 91 pop r19 aa: 2f 91 pop r18 ac: 0f 90 pop r0 ae: 0f be out 0x3f, r0 ; 63 b0: 0f 90 pop r0 b2: 1f 90 pop r1 b4: 18 95 reti 000000b6 <ioinit>: } void ioinit (void) /* Note [6] */ { /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */ TCCR1A = TIMER1_PWM_INIT; b6: 83 e8 ldi r24, 0x83 ; 131 b8: 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; ba: 8e b5 in r24, 0x2e ; 46 bc: 81 60 ori r24, 0x01 ; 1 be: 8e bd out 0x2e, r24 ; 46 /* * 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; c0: 1b bc out 0x2b, r1 ; 43 c2: 1a bc out 0x2a, r1 ; 42 /* Enable OC1 as output. */ DDROC = _BV (OC1); c4: 82 e0 ldi r24, 0x02 ; 2 c6: 87 bb out 0x17, r24 ; 23 /* Enable timer 1 overflow interrupt. */ TIMSK = _BV (TOIE1); c8: 84 e0 ldi r24, 0x04 ; 4 ca: 89 bf out 0x39, r24 ; 57 sei (); cc: 78 94 sei ce: 08 95 ret 000000d0 <main>: } int main (void) { d0: cf e5 ldi r28, 0x5F ; 95 d2: d4 e0 ldi r29, 0x04 ; 4 d4: de bf out 0x3e, r29 ; 62 d6: cd bf out 0x3d, r28 ; 61 ioinit (); d8: ee df rcall .-36 ; 0xb6 <ioinit> /* loop forever, the interrupts are doing the rest */ for (;;) /* Note [7] */ sleep_mode(); da: 85 b7 in r24, 0x35 ; 53 dc: 80 68 ori r24, 0x80 ; 128 de: 85 bf out 0x35, r24 ; 53 e0: 88 95 sleep e2: 85 b7 in r24, 0x35 ; 53 e4: 8f 77 andi r24, 0x7F ; 127 e6: 85 bf out 0x35, r24 ; 53 e8: f8 cf rjmp .-16 ; 0xda <main+0xa> 000000ea <__bad_interrupt>: ea: 8a cf rjmp .-236 ; 0x0 <__heap_end>
avr-objdump
はとても有用ですが、時にリンカによって生成されるリンクに関する情報が必要になることがあります。"Map
file"はこれらの情報を含んでいます。マップファイルはあなたが作ったコードやデータのサイズをモニタするのに有用です。また、各モジュールがどこにロードされたか、ライブラリのどのモジュールがロードされたかを教えてくれます。これはあなたのアプリケーションに対し別の視点を与えてくれます。マップファイルを得るために、私は通常-Wl,-Map,demo.map
オプションをリンクコマンドに加えています。demo.mapを得たいときは下記のコマンドでアプリケーションを再度リンクしてください。(その一部を以下に示します)
$ avr-gcc -g -mmcu=atmega8 -Wl,-Map,demo.map -o demo.elf demo.o
いくつかの面白いことが demo.mapからわかります。
.rela.plt *(.rela.plt) .text 0x00000000 0xec *(.vectors) .vectors 0x00000000 0x26 /junk/AVR/avr-libc-1.4/avr/lib/avr4/atmega8/crtm8.o 0x00000000 __vectors 0x00000000 __vector_default 0x00000026 __ctors_start = .
.
text
ステートメント (プログラム命令が保存されるところ) は0番地から始まっています。
*(.fini2) *(.fini1) *(.fini0) 0x000000ec _etext = . .data 0x00800060 0x0 load address 0x000000ec 0x00800060 PROVIDE (__data_start, .) *(.data) *(.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 *(COMMON) 0x00800063 PROVIDE (__bss_end, .) 0x000000ec __data_load_start = LOADADDR (.data) 0x000000ec __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 = .
.
text
セグメントの最終アドレスは0x114
です。(_etext
で示されています)。命令は フラッシュを276バイト使っています。.
data
セグメント(static指定された初期化されている変数が保存されている)は0x60番地から始まっている。これはATmega8 プロセッサのI/Oレジスタがある場所の直後にあたります。.data セグメントの次の利用可能アドレス(※ここでは次の.bss) もまた0x60である。このアプリケーションは初期化済みデータを持たないようです。
.
bss
セグメント (初期化されないデータが保存されている場所) は0x60番地から始まっています。.
bss
セグメントの次の利用可能なアドレスは0x63です。アプリケーションは3バイトの初期化されていないデータを持つことがわかります。.
eeprom
セグメント(EEPROM上変数が保存されるところ)は0x00番地から始まっています。.
eeprom
セグメントの次の利用可能なアドレスもまた0x00番地です。このアプリケーションはEEPROM上変数を使っていないようです。
さて、バイナリ形式のアプリケーションができました。しかしこれをどうやってプロセッサに書き込めばいいのでしょうか?多くの(すべてではない)プログラマ(書き込み器)はGNUの実行形式ファイルを受け付けてくれません。そこで、もう少し加工してやる必要があります。次のステップは実行されるバイナリ部分を取り出し、 .hex 形式ファイルに納めることです。GNU ユーティリティは avr-objcopy というツールでそれを行います。
以下のコマンドにより、ROMに納めるべき内容が私たちのプロジェクトのバイナリ出力から取り出され、demo,hexファイルに書き込まれます。
$ avr-objcopy -j .text -j .data -O ihex demo.elf demo.hex
demo.hex
ファイルの中身です。(※テキストファイルです)
:1000000012C073C072C071C070C06FC06EC06DC0CE :1000100011C06BC06AC069C068C067C066C065C0F7 :1000200064C063C062C011241FBECFE5D4E0DEBF50 :10003000CDBF4EC01F920F920FB60F9211242F9377 :100040003F938F939F93809162009927009739F097 :100050000197B9F0209160003091610021C020919A :100060006000309161002F5F3F4F3093610020931B :10007000600083E02F3F380799F481E080936200AD :100080000FC02091600030916100215030403093CA :100090006100209360002115310511F41092620077 :1000A0003BBD2ABD9F918F913F912F910F900FBE25 :1000B0000F901F90189583E88FBD8EB581608EBD1F :1000C0001BBC1ABC82E087BB84E089BF789408958A :1000D000CFE5D4E0DEBFCDBFEEDF85B7806885BF5A :0C00E000889585B78F7785BFF8CF8ACF51 :00000001FF
-j .text -j .data
オプションは .text と .data セグメントから抽出した情報を出力するよう指示します。もしEEPROMセグメント指定すれば、EEPROMに書き込むための .hex ファイルが得られます。
$ avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex demo.elf demo_eeprom.hex
これによって得られた
demo_eeprom.hex
の中身です。
:00000001FF
これは中身が空っぽの .hex ファイルです。(我々はEEPROM変数を指定しなかったのですから、これは予想されることです)
これらのコマンドを繰り返し打ち込むより、makefileに置き換えてしまった方がいいです。デモプロジェクトをmakeを使って組み立てるには、まず以下のような内容をMakefileとして保存します。
※ makefileは現在のavrgcc(少なくとも20040720版以降)では、Mfileという便利なツールで自動生成することができるようになりました。 WinAVRの標準インストールではデスクトップにショートカットができています。
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 = atmega16 #MCU_TARGET = atmega163 #MCU_TARGET = atmega164 #MCU_TARGET = atmega165 #MCU_TARGET = atmega168 #MCU_TARGET = atmega169 #MCU_TARGET = atmega32 #MCU_TARGET = atmega324 #MCU_TARGET = atmega325 #MCU_TARGET = atmega3250 #MCU_TARGET = atmega329 #MCU_TARGET = atmega3290 #MCU_TARGET = atmega48 #MCU_TARGET = atmega64 #MCU_TARGET = atmega644 #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 $< $@ %_eeprom.srec: %.elf $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ %_eeprom.bin: %.elf $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ # 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 $< $@