普段使っているArduino UNOに搭載されたATmega328というMCUは、旧ATMEL社のmegaシリーズ型番#328と言う意味です。
ATmega328も28DIP(Dual Inline Package 28 pin)ということで、十分小さいのですが、328duinoの様に170穴ミニブレッドボード1枚に載せようとすると、ブレッドボード上17列しかない穴の14列をATmega328が占めてしまって、外部回路を構築するスペースが確保できず、苦労します(下図がその例)。
上左図はミニブレッドボードに押し込んだUNOの互換機(328duino(第一形態))で25MHzまでクロックアップ可能な機体、上右図はミニブレッドボードに押し込んだ赤外線リモコン化328duinoです。いずれも、ちょっとした外部回路しかついていないのですが、無理をしてギチギチに詰め込んでいます。
もっと小さく🐜使えるMCUは無いものか?
ATmega328だって、28DIP、Flash 32Kバイト、RAM 2Kバイトと、十分小さいのですが、にもかかわらず、mega(巨大な)という名を冠しているわけです。mega(巨大な)があれば、対応するtiny(とても小さい)MCUシリーズも存在しています。
今回入手したのは、そんなMCUシリーズの中からATtiny2313というMCUです(下図)。
ATtiny2313のデータシートを検索する(別ウインドウが開きます)
簡単に仕様をまとめておきます(↓)。
ATtiny2313 | ||
項目 | 概要 | 備考 |
MCU | ATtiny2313A-PU @4〜20[MHz] |
内蔵RC発振器8MHzでの動作が基本。 外部発振器を接続すると、20MHz動作可能。 |
CPU | 8ビットRISC AVRコア |
ATmega328と同じAVRコア。 いくつかサポートの無い命令が存在する。 |
Flash | 2Kバイト | 2Kバイトしかないので、ブートローダ領域は無い。 |
RAM | 128バイト | 128Kバイトではなくて、128バイト。とても小さい。 |
EEPROM | 128バイト |
同上、RAMと同じサイズって……。 RAMの方が小さすぎるのだけど……。 |
PKG | 20DIP | 端子機能表は後ほど掲載。 |
電源・I/O | 1.8〜5.5[V] | ATmega328と同様、電圧-fMAX規定がある。 |
I/Oユニット |
8 bit Timer TC0 16 bit Timer TC1 Analog Comparator USI(Universal Serial Interface) USART |
4本のPWM出力。 AD変換器は非搭載。 アナログ比較ユニットがある。 USIでSPI、I2C通信が可能。 USARTハードウェアシリアルも搭載。 |
28DIPのATmega328P-PUに対して、こちらは20DIPなので、端子数にして8本、ブレッドボードの占有列数にして4列分コンパクトに使うことが出来ます。Flash 2Kバイトなので、ブート領域がありません(↓)。
UNOのブートローダ:
てな訳で、ATtiny2313への書き込みは、ISP(In System Programmer、書き込み装置)を使ったシリアルプログラミング(SPI通信)が基本です。Arduino UNO等をISPとして使い、ArduinoISP.ino等で書き込むことになります。後ほど、書き込み方を説明します。
シリアルプログラミング(ISP)という事は、Arduino IDEの「1発コンパイル&書き込み」ボタン(下図)では書き込むことが出来ないのだけど、とは言っても、ATtiny用のボードをIDEに追加しておかないと、コンパイルすらできないので、まずはボードを追加します。
Arduino IDEを起動して、「ファイルメニュー ⇒ 環境設定」を選択すると、以下の様なウインドウ「環境設定」が表示されます。
「追加のボードマネージャのURL:」欄右端のボタンを押して、「追加のボードマネージャのURL」ウインドウが開いた状況が上図です。こっちのウインドウの「クリックして非公式〜」の文字列部分がクリックできるようになっているので、クリックしてください。
WEBブラウザが開いて、非公式ボードの一覧が表示されるので、この中から「ATtiny」や「ATtiny2313」等のキーワードで適切なボードを調べて、そこに記載されたURL「http〜.json」をコピーし、「追加のURL」欄に貼り付けます。
本ページではATTinyCoreというボードパッケージを使いました。URLは上図の通り。他のパッケージを使うと、以下の解説と異なる内容があるかもしれませんので、ご注意ください。
これ(↑)だけだと、まだボードの追加は完了していません。
「ツールメニュー ⇒ ボード ⇒ ボードマネージャ」を開き、「ATTinyCore」で検索すると、上で追加したボードが見つかるので、適切なバージョンを選択して「インストール」してください。これで追加完了です。要らなくなったら、ここで「削除」もできます。
試しにUNO用に作った小さめのプログラムをコンパイルしてみたのですが、ほとんどのプログラムはコンパイル失敗でした……。
試してみたこと:
「失敗」と言うのは正しくないかな?コンパイル自体は、正常に終了します。素晴らしいです。しかし、ほとんどのプログラムが「スケッチが大きすぎます」と言うエラーになります(↓)。
Arduino:1.8.11 (Windows 10), ボード:"ATtiny2313(a)/4313 (No bootloader), Enabled, ATtiny2313/ATtiny2313A, 8 MHz (internal), EEPROM retained, B.O.D. Disabled (saves power), no, Enabled, Port A (pins 2,3,17)" 最大2048バイトのフラッシュメモリのうち、スケッチが3958バイト(193%)を使っています。 最大128バイトのRAMのうち、グローバル変数が317バイト(247%)を使っていて、ローカル変数で-189バイト使うことができます。 text section exceeds available space in board スケッチが大きすぎます。http://www.arduino.cc/en/Guide/Troubleshooting#size には、小さくするコツが書いてあります。 ボードATtiny2313(a)/4313 (No bootloader)に対するコンパイル時にエラーが発生しました。 |
上のエラーメッセージの例だと、Flash 2Kバイトに対して約4Kバイトになっているし、RAM 128バイトに対して317バイト必要、と言うことで、Flash/RAM共、全く足りていません。小さ目なプログラムを選んだつもりでしたが、ATtiny2313にとっては、まだまだ大きかった様です。
そこで、ライブラリの物量をチェックしてみました。
まず、中身が空っぽのプログラムを用意します(↓)。
void setup (void)
{ // empty
}
void loop (void)
{ // empty
}
これをコンパイルしてメモリ(Flash/RAM)使用量をメモします。これをベースに、「ツールメニュー」のATtiny2313に関するオプションを変えてみたり、Serialライブラリを使用したり、Wireライブラリを使用したりするコードを追加して、Flash/RAMの物量を調べました。
condition | Flash | RAM | memo |
ATtiny2313_default | 236 | 9 |
2313を選択したデフォルト状態 空のsetup()、loop()関数 |
init 2nd timer | 240 | 9 | ATtiny2313_defaultに対して2nd timerを有効化 |
millis disable | 66 | 0 | ATtiny2313_defaultに対してmillis()関数を無効化 |
Serial "hello, world" | 1,096 | 92 |
ATtiny2313_defaultに対して Serialライブラリを使った"hello, world" |
Wire "hello, world" | 964 | 64 |
ATtiny2313_defaultに対して Wireライブラリを使った"hello, world" |
sprintf "hello, world" | 308 | 23 |
ATtiny2313_defaultに対して sprintf()関数を使った"hello, world" |
評価に使用したプログラムをリンクしておきます。
評価用プログラム:tiny_test.ino
「hello, world」等のコードはプログラム先頭付近の「#define」マクロで選択可能にしてあります(↓)。
//#define USE_SERIAL
//#define USE_WIRE
//#define USE_SPRINTF
//#define APLI_FIREFLY
4つの#defineの内、必要な物のコメントだけを外してコンパイルしてメモリ使用量をメモしました。
雑感:
判ったことは、Serialライブラリの物量が大きいという事です。Serialライブラリを使った時点で、Flash容量の半分以上、RAM容量の70%以上を持って行かれます。本HPのUNO用プログラムのほとんどはSerialライブラリを使い、デバッグ用のコマンドや表示をハンドリングしているので、コンパイルしても容量不足となってしまったのでしょう。
解析と言っても、さらっとコードを眺めてみただけです(1^^;)。「ATTinyCoreで何ができるのだろう?」と言うのが興味の対象です。ATTinyCoreのインストールパッケージは以下の場所にあります。
<User>\AppData\Local\Arduino15\packages\ATTinyCore\
<User>は、Windowsのユーザディレクトリです。ユーザ名やWindowsエディションによって、若干パスが異なる場合があります。<User>\AppDataは通常、不可視ディレクトリなので、エクスプローラなどからは見えていません。不可視属性のファイル/ディレクトリを表示する設定に変更して辿ってください。
以下の様なことが判りました。
Port | DIP | ATtiny2313 | DIP | Port |
D17(PA2/PCINT10/RESET/dW) | 1 | ● | 20 | VCC |
D0(PD0/PCINT11/RXD) | 2 | 19 | D16(PB7/USCK/SCL/SCK/PCINT7) | |
D1(PD1/PCINT12/TXD) | 3 | 18 | D15(PB6/MISO/DO/PCINT6) | |
D2(PA1/PCINT9/XTAL2) | 4 | 17 | D14(PB5/MOSI/DI/SDA/PCINT5) | |
D3(PA0/PCINT8/CLKI/XTAL1) | 5 | 16 | D13(PB4/OC1B/PCINT4) | |
D4(PD2/PCINT13/CKOUT/XCK/INT0) | 6 | 15 | D12(PB3/OC1A/PCINT3) | |
D5(PD3/PCINT14/INT1) | 7 | 14 | D11(PB2/OC0A/PCINT2) | |
D6(PD4/PCINT15/T0) | 8 | 13 | D10(PB1/AIN1/PCINT1) | |
D7(PD5/PCINT16/OC0B/T1) | 9 | 12 | D9(PB0/AIN0/PCINT0) | |
GND | 10 | 11 | D8(PD6/ICPI/PCINT17) |
※D#は、Arduino IDEのデジタル端子番号。
※1ピンはRESET端子として割り当てておかないと、シリアルプログラミングモードで書き込みできない。
お試しにATtiny2313で実行できるような小さな(シリアルモニタを使わない)アプリケーションって、何かなかったかな?と過去記事を漁ったところ、「#4-6 LEDキャンドル」なんて良いんじゃないの?と、思ったのだけど、ダメ、でした。1/fゆらぎ乱数列4,096点分のデータが4Kバイトにもなり、ATtiny2313のFlashには半分も載りません。アララ……。
1/fゆらぎ乱数列のデータ数を間引いて、ATtiny2313に載るように工夫することも可能だろうけど、それはまた別なテーマとします。
※「#14-6 暗くなったら蝋燭を灯して」にて、ATtiny2313を使ったLEDキャンドルを作りました。ご確認ください。
今回は、ATtiny2313の内蔵プルアップ抵抗で明滅させる「蛍の様なLED」を作って見ようと思います。
蛍の様な明滅をさせるためにsin()関数を使ったところ、Flashが足りなくなりました。float型の演算ルーチンと、float型数学関数のコードサイズがかなり大きいみたいです。止むを得ず、uint8配列128個を使った整数型のテーブル(uint8 sin_tab[128])を使いました。128個なら128バイトなので、float型の物量に比べればかわいいものです。
あと、どうも16ビットタイマ(TC1)がToneライブラリ用に初期化されているみたいで、PWMが出力されませんでした。setup()関数に以下の記述を追加して、TC1をPWM出力可能な設定に変更しました。
// TC1 setting
// pwm phase correct 8 bit mode 1, fcpu/64
TCCR1A=0x01;
TCCR1B=0x03;
※「ツールメニュー ⇒ Initialize Secondary Timers」の設定が関係するのではないか?と思いましたが、yes/noどちらでもPWM出力できませんでした。
setup()関数内で上の設定をしておきさえすれば、その後は普通にanalogWrite()関数でPWM出力を制御できます。TC1が出力するI/Oポートはこちらを参照して、D12(OC1A)とD13(OC1B)が対象です。
完成したプログラムをリンクしておきます。
完成プログラム:tiny_test.ino
先頭付近の#defineを以下の様にしてコンパイル&書き込んでください。コンパイル&書き込みの方法については、次節で解説します。
//#define USE_SERIAL
//#define USE_WIRE
//#define USE_SPRINTF
#define APLI_FIREFLY
手元の環境では、Flash 1,262バイト、RAM 9バイトを要しました。大したプログラムでもないのに、Flashは半分以上を使っています。どのプログラムでも、ちょっとした関数を呼ぶとFlashの半分は埋まってしまうので、共通して使用される何らかのコードサイズが既に1Kバイト程度存在している、と考えるのが自然ですね。
ATtiny2313へArduino IDEからプログラムを書き込むには、別途、Arduino UNO等の機体が必要です。例えばUNOを「Arduino as ISP」として使って、ATtiny2313へ書き込みます。以下に手順を示します。
手元の環境では、「スケッチメニュー ⇒ 書込装置を使って書き込む」がエラーになって書き込みできませんでした。Arduino IDEの書き込みツール(avrdude)が「attiny2313aなんて知らないよ」ってエラーでした。理由は解りません(※)が、以下の様に修正したところ、正常に書き込み出来るようになりました。
修正するファイル:
<User>\AppData\Local\Arduino15\packages\ATTinyCore\hardware\avr\1.4.1\
boards.txt
修正内容:
attinyx313.menu.chip.2313.build.mcu=attiny2313a
↓
attinyx313.menu.chip.2313.build.mcu=attiny2313
最後の"a"を消してセーブしたら、Arduino IDEを一旦閉じて再度起動してください。
※使用中のArduino IDE 1.8.11に含まれる書き込みツールavrdudeのバージョンが古いのか?/合っていないのか?
UNOからATtiny2313へ書き込む時の接続表を示します。
UNO | 接続 | ATtiny2313 | |
5V | → | 20 | VCC |
GND | ⇔ | 10 | GND |
D10 | → | 1 | RESET |
D11(MOSI) | → | 17 | D14(MOSI) |
D12(MISO) | ← | 18 | D15(MISO) |
D13(SCK) | → | 19 | D16(SCK) |
※ATtiny2313端子名左の数字は20DIPのピン番号です。生のMCUをブレッドボードに挿して使うので、Arduinoのピン番号(D14等)よりも、20DIPのピン番号で記した方が使い易いと思います。
UNOをISPとして使って、ATtiny2313へ書き込み中の様子は下図の様になります。
「スケッチメニュー ⇒ 書込装置を使って書き込む」がボタンに割り当たっていないため、一々メニューから選ばないとならないのが難点ですが、それ以外はUNOでのプログラミングと変わりません。アプリケーションでSPI端子を使わないのであれば、上図の様に繋ぎっぱなしでデバッグすることができます。
ATtiny2313にも、ATmega328と同じ様にヒューズが入っています。購入したままのATtiny2313は、工場出荷時設定のままでクロックが1MHz動作になっているなど、ATTinyCoreの環境と合っていません。ATTinyCoreを使って開発を続けるためには、少なくとも1回、以下の手順に従ってヒューズの書き込みを実行してください。
ATtiny2313へのヒューズ書き込みは、Arduino IDEの「ツールメニュー ⇒ ブートローダを書き込む」で書き込むことが出来ます。書き込み可能なヒューズ値は、以下の通りです。
3つの項目だけですが、実際、個別に書き込みが必要なパラメータは、この3つで充足しています。メニューから設定したい項目を選んで、「ツールメニュー ⇒ ブートローダを書き込む」を実行するだけです。何を設定して良いかわからない場合には、とりあえずデフォルトのまま(上図がデフォルト)で書き込みを実施してください。
以下にヒューズ書き込み手順を示しておきます。
なお、「ツールメニュー ⇒ ブートローダを書き込む」を実行すると、ATtiny2313のFlashに書き込まれていたプログラムは消去されます。たぶんChip Eraseなので、「EEPROM not retained」に設定されていた場合には、EEPROMの内容も消去されます(EEPROM未使用のため未確認)。
前節の作業でプログラム(tiny_test.ino)を書き込んだATtiny2313を使って、以下の回路をブレッドボードに構築します。
デバッグ中は、ISP端子(RES,SCK,MISO,MOSI)を繋ぎっぱなしにできるよう、リザーブしておきました。
ATtiny2313の内蔵プルアップ抵抗で制限された電流を使って4つのLEDを明滅させるので、とても暗いです。データシートによると、プルアップ抵抗値Rpu=20〜50[kΩ]ですので、Vf≒2VのLEDとVcc=5Vで動作させたとして、流せる電流値=(Vcc−Vf)/Rpu≒60〜150[μA]であり、明るいはずがありません。
ただ、LEDの発光色によって、同じ電流値でも明るさが異なりますので、なるべく明るく光るLEDを選ぶのが吉です。手持ちの部品では、緑色や白色の方が、赤色や青色LEDよりも明るく光りました。
実験中の様子を下図に示します。
4つのLEDが蛍の様に明滅するはずです。すべて異なる周期で明滅させているので、4匹の蛍がブレッドボードに止まっているみたいじゃありませんか?……ちょっと、色が蛍っぽくないかな……。
※本物の蛍が複数匹近くにいると、明滅のタイミングが揃っていくらしいです。今回のプログラムでは、4個の明滅周期をわざと変えているので、バラバラに明滅します。
電源はeneloop(1.2V)×4本≒4.8Vを使いました。ちょうど良い感じの明るさで明滅しています。
まずはめでたく、ATtiny2313にArduino IDEからプログラムを書き込んで、思い通りに動かせることを確かめられました。良かった。
問題はメモリの少なさ、ですね。結局、Flash 2Kバイト、RAM 128バイトでどこまでできるのでしょうか?
ATtiny2313は20ピンの内、電源(VCC,GND)で取られるのは2本だけなので、残り18本ものI/Oポートを使えるのですが、それを使いこなそうとすると、あまりにメモリが少ない。ちょっと、I/Oポート数とメモリ容量のバランスが取れていない様に感じます。
限られたリソースを如何に効率よく使ってアプリケーションを成立させるか?って言うのもやりがいのあるテーマですので、機会を見つけて挑戦してみようと思います。
今回のお買い物は、以下の通りです。
追加購入したATmega328と一緒に購入しました。この金額だと、費用対効果は圧倒的にATmega328の方が優れていると思います。……I/Oポート数とメモリ容量のバランスも良いし、さすがUNOのメインMCUに選定されただけのことはあります。
あまりにメモリが少なくてデバッグにシリアルモニタが(ほぼ)使えないなど、かなり制限される分、腕の見せ所ですね……。メモリが足りないことで大規模なシステムは作れないので、腕を見せる前にシステムが成立しないかもしれませんけれど(1^−^;)。