いまさらATTINY2313

ジャンク箱を漁っていたら、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です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)