Edited at

Arduinoで、小っちゃいIC(ATTiny)を使う方法

More than 3 years have passed since last update.

ArduinoUNO


Arduinoで、小っちゃいIC(ATTiny)を使う方法


ちっちゃいICを使いたい!

Arduino、始めたばかりですが超楽しいです。(いや始めたばかりだから楽しいのかw)

お金が無いせいで、シールドとかにはまだ手を出してなくて、Arduino UNOとブレッドボード、それと安く手に入る部品だけを使って遊んでいます。

ブレッドボードで動作確認したら、ICをUNOから引き抜いて、ユニバーサル基盤に作った回路に組み込んでみたり。こうした使い方をしていることもあり、ATmega328Pを追加で買ったりしてるのですが、ATmega328Pは1コ250円もして、少々お高いのが気になってました。

それに28PIN-DIPって簡単な用途には大きくて配線もたいへん。

将来量産(なにを?!いや何か絶対!w)を見据え、もっと物理的にちっちゃいICを試してみたい。でも表面実装(SMD)版とかハンダ付け超ムリ!


ATTinyはDIPでもちっちゃい!

というわけで目をつけたのがATTinyシリーズ。DIP版でも足の数が少ないので、ちっちゃいです。特に13A!

Writer

上からATTiny13A、ATTiny2313、一番下がATmega328P

試しに20本足のATTiny2313-20PUと、8本足のATTiny13Aを購入してみました。

ざっくりSPEC表

SPEC
ATmega328P
ATTiny2313
ATTiny13A

価格
(2014.1.26現在)
250円
150円
50円

DIP PIN数
28
20
8

ROM
32Kbyte
2Kbyte
1Kbyte

RAM
2Kbyte
128byte
64byte

ATTinyは大きさだけでなく、性能も、ちっちゃい!w

どこまで使えるかわかりませんが、ちょっと触ってみようと思います。


1. Arduino UNOで、ちっちゃいIC(ATTiny)を開発する

Arduino UNOがあれば(それが互換機でも)ATTinyの開発ができます。UNO以外に必要なのは、ブレッドボードと抵抗、コンデンサ、ジャンパーピン、LEDなど。Arduino入門編をやってる方は持っているものばかりだと思います。

Arduino UNOでATTinyなどを開発するやり方は、kosakalabさんの、下記記事で超詳しく紹介されています。

Arduino IDEでATtiny他の開発(Arduino-ISP編)

私も記事の通りに進めることでATTiny13AとATTiny2313でのLチカに成功しました。

素晴らしい記事を書いてくれたkosakalabさんには、本当に感謝です!


2. ROM/RAM使用量を確認できるようにする


ROM使用量はIDEでビルドするだけで表示される

ATTinyはROM(正確にはFLASHROM)やRAM(正確にはSRAM)が小さいので、プログラムも小さくしなければ入らなくなります。

Arduinoのプログラムが、どのくらいROMやRAMを消費するか、Arduinoのいわゆる「Lチカ(blink)」を使って確かめてみましょう。

** Arduino IDEに付属のBLINKのコード (コメントは外してあります) **

(ATTiny対応にPIN番号だけ変更してあります。)

    int led = 0;    // ATTiny2313用に変更

void setup() {
pinMode(led, OUTPUT);
}

void loop() {
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}

これをビルドしてみます。すると、下記のような表示が。

910byte

910byte。え!Lチカだけで?こんなに大きいんだ。。。13A、Lチカ以外できないんじゃ?

と、一抹の不安を感じつつ。。

910byteというのはFLASH ROMの使用量です。まだRAMはどのくらい使ってるのかは解りません。


RAM使用量の調べ方

というわけで、RAM使用量も確認してみます。

ArduinoのRAM使用量の調べかたは、下記で紹介されています。

Arduinoでのメモリ使用率の調べ方

なんと、elfファイルからdumpしろ。ということらしいですね!

いきなり組み込み系な感じになって来ました。なんかArduinoの可愛さが薄れていくような。。。w

いえいえ、ちっちゃいATTinyのためです。やってみましょう。

> avr-objdump -h プロジェクトファイル名.elf

と実行してみます。

objdump

Lチカのプログラムだけですと、.textと.bssしか出てきませんでした。よく見るとconstデータがありませんね。

これだとちょっと説明にならないので、少しプログラムを改造して、もう1度やってみます。

(Lチカのタイミングを少しおかしくしただけのものです)

    int cnt;

const int led = 0;
const int a[] = {200,400,800,1000}; // Lチカのタイミング

void setup() {
pinMode(led, OUTPUT);
}

void loop() {
int v;
v = a[cnt];
cnt++;
if(cnt == 4){
cnt = 0;
}
digitalWrite(led, HIGH);
delay(v);
digitalWrite(led, LOW);
delay(v);
}

今回は、LEDのピン番号の変数と

このスケッチをビルドすると、Lチカタイミング用のテーブルをconstに追加してみました。

これをビルドしてみますと、

982byte

objdump

となりました。const変数を追加したので、今度は.dataというものが出てきました。

長くなりましたが、ROMとRAMの容量をまとめると、

ROM使用量 = .textと.dataの合計 (今回の例では 982byte)

RAM使用量 = .bssと.dataの合計 (今回の例では 19byte)

となります。

(RAMはもう少し複雑で、ヒープやスタックと共有で利用していますので、正確に言うと上記以外にも使われています)

RAMの使われ方については、下記サイトの説明がとても参考になります!

Arduino Unoのメモリ


3. プログラムをちっちゃくする!

さて、いよいよプログラムをちっちゃくするのに挑戦します!

そんなんかんたんにできるんか〜?と気になるところですが、さすがはArduino、さすがはインターネット!

いろいろな手法があるようです。


ROMを減らす方法。単純に処理する関数をつくろう。


簡単にやれることから

Arduinoでは、いろいろな関数やライブラリが利用できます。

これらは、とても「書きやすく、とっつきやすい」ものですが、中を覗いてみると、様々なケースに対応できるようにするためか、結構なコード量だったりします。

先ほどのLチカにも出て来たpinMode()digitalWrite()を置き換えるだけでも結構削減できるので、やってみたいと思います。


pinMode()digitalWrite()がどのような処理を行っているかについては、下記サイトで詳細に解説されています。


Arduionoソフトウェアの内部構造: pinMode()

Arduionoソフトウェアの内部構造: digitalWrite()

上記サイトの情報をもとに、pinMode()とdigitalWrite()を書き換えてみます。

実はポート叩いてるだけなんで、マクロを作ります。

    // ATTiny2313用。D0ピンIOマクロ

#define PMD0_OUTPUT() (DDRD |= _BV(0)) // pinMode(0,OUTPUT)
#define DWD0_HIGH() (PORTD |= _BV(0)) // digitalWrite(0,HIGH)
#define DWD0_LOW() (PORTD &= ~_BV(0)) // digitalWrite(0,LOW)

あとは、pinMode()やdigitalWrite()と置き換えるだけ。

    int cnt;

// const int led = 0; // ←マクロにしたので要らなくなりました。
const int a[] = {200,400,800,1000}; // Lチカのタイミング

void setup() {
PMD0_OUTPUT(); // pinMode(0,OUTPUT)
}

void loop() {
int v;
v = a[cnt];
cnt++;
if(cnt == 4){
cnt = 0;
}
DWD0_HIGH(); // digitalWrite(0,HIGH)
delay(v);
DWD0_LOW(); // digitalWrite(0,LOW)
delay(v);
}

でビルドしてみると。

624byte

982→624byte!300byte以上も削減できました。


トレードオフできることはトレードオフしてみる。

さきほどのpinMode()とdigitalWrite()の例は、もとある機能を変えずにコード圧縮を実現していました。

しかし、全ての機能を正確に実現することは、そんなに大事でないかもしれません。

たとえば、さきほどのLチカ。

Lチカであれば、LEDの間隔が多少ずれても、そんなにたいしたことではないはずです。

精密なタイマーの場合は大問題ですが、そもそも精密なタイマーにdelay()は使わないでしょうw

というわけで、delay()にもメスを入れてみます。かなり大胆にw

    extern volatile unsigned long timer0_millis;

void deeeelay(unsigned long ms) {
unsigned long exptim = timer0_millis+ms;
while (timer0_millis < exptim){}
}

もう、あまり人におススメできる内容から脱線している気がしますw

冒頭で外部参照しているtimer0_millisは、hardware/arduino/cores/arduino/wiring.c

などで宣言されているグローバル変数で、起動時からの経過時間(但し限界まで行くと0に戻りまたカウント)をmsで記録しています。millis()を使っても良いのですが、関数呼ぶ分オーバーヘッドがあるので、直接参照しています。

で、中は見ての通りで、指定された時間が来るまで永久ループしてるだけです。

で、コレの何が「トレードオフ」なのかと言うと、delay()の1/1000の精度しかありません。気になる方はdelay()側のコードを見てください。

上記を使うコード部は、delay(v)deeeelay(v)にするだけなので、割愛します。

ビルドしてみると、

530byte

530byte!

さらに100byte近く削減できました!

これ以上は、違うところ(本来触ってはいけないところ)を触らなければいけなさそうな気がします。。ので、そろそろ止めときます。(ここに書くのではなく、自分で勝手にコッソリ進めますw)

※実は今、ATTiny13AでTone()ぽいものを無理矢理動かしたくて、wiring.cを改造しまくってたり。。。


RAMを減らす方法。.dataを減らしてROMへ。PROGMEMを使う。

さて、最後に.dataが使用するRAM容量を減らす方法を紹介して、この記事をしめたいと思います。

.dataはROMにも入ってるくせにRAMにも入っているという邪魔者です。

「データは入れときたいけど、使うときすぐに使いたいから起動するときROMからRAMに読み込んどく」ということをやってるかららしいです。

でも、大きなデータって主に配列だと思うのですが、これ、全部の要素を頻繁に上から下まで順番に読み込む、なんて使い方するんでしょうか。普通はあまりしないと思います。

通常は、何番目のデータを読むか?という指定をして、ダイレクトに使う部分のデータだけを読み込むことの方が多いと思います。こうした使い方だと、PROGMEMという方法が使えます。

使い方は、下記ページでとても詳しく教えてくれます。教わりました。

Arduino Unoのメモリ

※上記記事はArduino UNOを例に記載されていますので、ATTinyだとメモリ量やアドレスなどが異なります。


まとめ

ATTinyに入れられるのは、ちっちゃいプログラムだけ!ちっちゃいは正義!ということでした。

最後まで読んでいただき、ありがとうございました!

間違いなど、ありましたら教えて頂けたらうれしいです。どうかよろしくお願いします〜。

オマケ

Arduinoで作ったBeep音楽器です。(これはATTinyではなくATmega168P使いました)

beepSynth

[InstagramにショートMovie Upしてます!]http://instagram.com/p/inYeIpnCkl/


つづき


2014.12.17更新

つづきを書きました!いろいろあるATTinyを何点かピックアップして紹介してみました〜!