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

  • 65
    Like
  • 0
    Comment
More than 1 year has 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を何点かピックアップして紹介してみました〜!