ジャンク箱を漁っていたら、ATTINY2313がたくさん出てきたので消費することを考えてみました。開発環境はもちろんLinux Mint 20です。
開発環境をインストール
単純に
$ sudo apt-get install gcc-avr binutils-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"
を /etc/udev/rules.d/99-avrispmkII.rulsとして保存します。
(参考: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です。