ジャンク箱を漁っていたら、ATTINY2313がたくさん出てきたので消費することを考えてみました。開発環境はもちろんLinux Mint 20です。
開発環境をインストール
単純に
$ sudo apt-get install gcc-avr binutil-avr avr-libc avrdude
でいけます。あと、udevのルールに
# avrispmkII programmer rules (https://www.microchip.com/DevelopmentTools/ProductDetails/PartNO/ATAVRISP2) SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2104", GROUP="users", MODE="0666"
を追加しておきます(参考:https://aur.archlinux.org/packages/avrisp-udev/)
AVRISP mkIIの接続
AVRISP mkIIをつないで、Fuseビットを読み出してみます。
$ avrdude -P usb -c avrisp2 -p attiny2313 avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e910a (probably t2313) avrdude: safemode: Fuses OK (E:FF, H:DF, L:64) avrdude done. Thank you.
ヒューズビットの初期値は、拡張=FFH、上位=DFH、下位=64Hで、自己プログラミング不可、シリアルプログラミング許可、WDT無効、BOD禁止、PA2端子機能=RESET、システムクロックは8分周、システムクロック外部出力禁止、SUT=2(電源電圧の立ち上がりが遅い場合に対応)、CKSEL=4(内蔵OSC=8MHz)、ということになります。外部クリスタルを使う場合はCKSELを書き換える必要があります。ヒューズビットを書き換える場合には以下のようにします。
$ avrdude -p attiny2313 -c avrispmkII -B 125kHz -U lfuse:w:0x64:m -U hfuse:w:0xDF:m
サンプルソース
以下のソースで試してみます。
#include <avr/io.h> #include <util/delay.h> #define BBLED 3 int main(void) { DDRB = _BV(BBLED); for(;;){ PORTB ^= _BV(BBLED); _delay_ms(250); } }
コンパイル
以下でコンパイル、ディスアセンブルできました。
$ avr-gcc -g -O2 -mmcu=attiny2313 -DF_CPU=1000000UL -c -o led.o led.c $ avr-gcc -g -O2 -mmcu=attiny2313 led.o -o led $ avr-objcopy -j .text -j .data -O ihex led led.ihex $ avr-objdump -d led led: ファイル形式 elf32-avr セクション .text の逆アセンブル: 00000000 <__vectors>: 0: 12 c0 rjmp .+36 ; 0x26 <__ctors_end> 2: 17 c0 rjmp .+46 ; 0x32 <__bad_interrupt> 4: 16 c0 rjmp .+44 ; 0x32 <__bad_interrupt> 6: 15 c0 rjmp .+42 ; 0x32 <__bad_interrupt> 8: 14 c0 rjmp .+40 ; 0x32 <__bad_interrupt> a: 13 c0 rjmp .+38 ; 0x32 <__bad_interrupt> c: 12 c0 rjmp .+36 ; 0x32 <__bad_interrupt> e: 11 c0 rjmp .+34 ; 0x32 <__bad_interrupt> 10: 10 c0 rjmp .+32 ; 0x32 <__bad_interrupt> 12: 0f c0 rjmp .+30 ; 0x32 <__bad_interrupt> 14: 0e c0 rjmp .+28 ; 0x32 <__bad_interrupt> 16: 0d c0 rjmp .+26 ; 0x32 <__bad_interrupt> 18: 0c c0 rjmp .+24 ; 0x32 <__bad_interrupt> 1a: 0b c0 rjmp .+22 ; 0x32 <__bad_interrupt> 1c: 0a c0 rjmp .+20 ; 0x32 <__bad_interrupt> 1e: 09 c0 rjmp .+18 ; 0x32 <__bad_interrupt> 20: 08 c0 rjmp .+16 ; 0x32 <__bad_interrupt> 22: 07 c0 rjmp .+14 ; 0x32 <__bad_interrupt> 24: 06 c0 rjmp .+12 ; 0x32 <__bad_interrupt> 00000026 <__ctors_end>: 26: 11 24 eor r1, r1 28: 1f be out 0x3f, r1 ; 63 2a: cf ed ldi r28, 0xDF ; 223 2c: cd bf out 0x3d, r28 ; 61 2e: 02 d0 rcall .+4 ; 0x34 <main> 30: 0e c0 rjmp .+28 ; 0x4e <_exit> 00000032 <__bad_interrupt>: 32: e6 cf rjmp .-52 ; 0x0 <__vectors> 00000034 <main>: 34: 88 e0 ldi r24, 0x08 ; 8 36: 87 bb out 0x17, r24 ; 23 38: 98 e0 ldi r25, 0x08 ; 8 3a: 88 b3 in r24, 0x18 ; 24 3c: 89 27 eor r24, r25 3e: 88 bb out 0x18, r24 ; 24 40: e3 e2 ldi r30, 0x23 ; 35 42: f4 ef ldi r31, 0xF4 ; 244 44: 31 97 sbiw r30, 0x01 ; 1 46: f1 f7 brne .-4 ; 0x44 <__SREG__+0x5> 48: 00 c0 rjmp .+0 ; 0x4a <__SREG__+0xb> 4a: 00 00 nop 4c: f6 cf rjmp .-20 ; 0x3a <main+0x6> 0000004e <_exit>: 4e: f8 94 cli 00000050 <__stop_program>: 50: ff cf rjmp .-2 ; 0x50 <__stop_program> $ $ avrdude -P usb -c avrisp2 -p attiny2313 -U flash:w:led
最後の書き込みの部分は、HEXファイルを指定せず、バイナリを直接指定しても大丈夫です。
圧電ブザーを鳴らしてみる
LED点滅に加えて、圧電ブザーを鳴動させてみます。圧電ブザーはタイマ0で駆動し、PORT D5=OCR0B端子に接続、高速PWM設定でタイマ0を動作させて圧電ブザーへのデューティーも制御してみます。(デューティーを可変させるのは圧電ブザーではあまり意味がなかった・・・)
ソースコードは以下の通りです。
#include <avr/io.h> // /usr/lib/avr/include/ #include <util/delay.h> // FUSEは初期値のまま(E:FF, H:DF, L:64) // クロックは内蔵OSCで8MHz、システムクロック分周=8 #define BBLED 3 // PORT B3 にLED接続 #define BUZ 5 // PORT D5 に圧電ブザー接続 int main(void) { DDRB = _BV(BBLED); DDRD = _BV(BUZ); for(;;){ PORTB ^= _BV(BBLED); _delay_ms(250); if(PORTB & _BV(BBLED)){ // 高速PWM設定によるブザー駆動 TCCR0A = 0x23; // COM0B[1-0]=10,WGM0[1-0]=11 高速PWM動作 TCCR0B = 0x0A; // WGM02=1,CS[2-0]=2 分周=8分周=1MHz/64=125kHz OCR0A = 125; // FREQ 125kHz / 125 = 1000Hz / 250 = 500Hz OCR0B = 62; // DUTYを設定=VOLUME / COM0B=10 // OC0B output } else { TCCR0A = 0x00; // 出力停止 } } }
Makefileを用意する
ArchiLinuxのページにAVR用のMakefileのサンプルがあったので、それを参考にMakefileを用意してみました。
CC = avr-gcc OBJCOPY = avr-objcopy SIZE = avr-size NM = avr-nm AVRDUDE = avrdude REMOVE = rm -f MCU = attiny2313 F_CPU = 1000000 LFUSE = 0x64 HFUSE = 0xDF TARGET = firmware SRC = main.c OBJ = $(SRC:.c=.o) LST = $(SRC:.c=.lst) FORMAT = ihex OPTLEVEL = s CDEFS = CFLAGS = -DF_CPU=$(F_CPU)UL CFLAGS += $(CDEFS) CFLAGS += -O$(OPTLEVEL) CFLAGS += -mmcu=$(MCU) CFLAGS += -std=gnu99 CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums CFLAGS += -ffunction-sections -fdata-sections CFLAGS += -Wall -Wstrict-prototypes CFLAGS += -Wa,-adhlns=$(<:.c=.lst) LDFLAGS = -Wl,--gc-sections LDFLAGS += -Wl,--print-gc-sections AVRDUDE_MCU = attiny2313 AVRDUDE_PROGRAMMER = avrisp2 AVRDUDE_SPEED = -B 125kHz AVRDUDE_FLAGS = -p $(AVRDUDE_MCU) AVRDUDE_FLAGS += -P usb AVRDUDE_FLAGS += -c $(AVRDUDE_PROGRAMMER) AVRDUDE_FLAGS += $(AVRDUDE_SPEED) MSG_LINKING = Linking: MSG_COMPILING = Compiling: MSG_FLASH = Preparing HEX file: all: gccversion $(TARGET).elf $(TARGET).hex size .SECONDARY: $(TARGET).elf .PRECIOUS: $(OBJ) %.hex: %.elf @echo @echo $(MSG_FLASH) $@ $(OBJCOPY) -O $(FORMAT) -j .text -j .data $< $@ %.elf: $(OBJ) @echo @echo $(MSG_LINKING) $@ $(CC) -mmcu=$(MCU) $(LDFLAGS) $^ --output $(@F) %.o : %.c @echo $(MSG_COMPILING) $< $(CC) $(CFLAGS) -c $< -o $(@F) gccversion: @$(CC) --version size: $(TARGET).elf @echo $(SIZE) -C --mcu=$(AVRDUDE_MCU) $(TARGET).elf analyze: $(TARGET).elf $(NM) -S --size-sort -t decimal $(TARGET).elf isp: $(TARGET).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(TARGET).hex fuses: $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m release: fuses isp clean: $(REMOVE) $(TARGET).hex $(TARGET).elf $(OBJ) $(LST) *~
無事に、
$ make isp fuses
でビルドと書き込みができました。fusesは1回だけ指定すればOKです。