はじめに
こんにちは!
先日の記事でArduino関数の圧倒的容量の多さに絶望したくろべこでございます。
本日は使用容量のミニマム化と題して、基本的な関数とレジスタ操作での使用容量の違いを調べていきたいと思いますよ!
とはいっても先人の方々の知恵を借りつつ進めさせていただきますよ
本日の教科書はこれ【ATtiny13の日本語版データシート】
では早速!
digitalRead()編
準備&実験
早速進めていきますが、<avr/io.h>のヘッダファイルを使いたいと思います。
これはAVRマイコンのレジスタビット名とビット番号の対応が定義されております。今回使用していくATtiny13のポートはPORTBらしいので下記のようなレジスタマップになっております。
- DDRB:1を書き込むと出力。0を書き込むと入力。
- PORTB:DDRBが0の時(1:pullup,0:ハイインピ),DDRBが1の時出力(1:high,0:low)。
- PINB:入力用のレジスタだが、出力時PINB=1とすると出力が反転するので注意。
てことで、まずはLチカだよねってことで下記のようなコードを書いていきますよ。回路はPB3に適当な抵抗とLEDを繋いでみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <avr/io.h> #include <util/delay.h> void setup() { DDRB |= 0b00001000;//PB3出力設定 } void loop() { PORTB |= 0b00001000;//PB3出力HIGH //_delay_ms(1000);//1000ms遅延 //PINB |= 0b00001000;//PORTBの出力反転 //_delay_ms(1000);//1000ms遅延 } |
<手順>
- 単純に書き込んでみてください(PB3のLEDが光るはずです)
- コメントを外して書き込んでみてください(1秒間隔で点滅するはずです)
*さりげなく使っている<util/delay.h>はavrのdelayヘッダーファイルです。delay()と置き換えると多少容量圧縮になります。
考察
ではどれくらい容量圧縮できたか表にしてみますね。ちなみにヘッダーファイルとブランクのスケッチだけだと54byteでした。
pinMode() | DDRB | digitalWrite() | PORTB | deyay() | _delay_ms() | |
フラッシュメモリ | 84byte | 2byte | 66byte | 2byte | 62byte | 18byte |
すごい!!差は歴然ですね!スケッチ全体で比較すると、、、ほぼ1/3。
- Arduino関数使用:280byte
- レジスタ操作(本記事のコード):96byte
ちなみに出力反転に使用した【PINB=0b00001000】は2byteでした。
レジスタ操作とかとっつきにくいかなと避けていましたが意外と簡単にArduino関数を再現出来てびっくりです。続いてanalogRead編をどうゾ
analogRead()編
準備&方法
まずは手順をざっくりとしたAD変換の手順です。
- ADCSRレジスタでA/D許可,A/D変換クロック(変換スピード)を設定。
- ADMUXレジスタで基準電圧,変換結果の配置方法,AD変換ピン設定。
- ADCSRAレジスタ6bit目が1でAD変換開始。
- ADCSRAレジスタ4bit目が1になることで変換完了。
- ADCL,ADCHからアナログ値取得。
詳細はデータシートの14. A/D変換器を読むとさらに理解が深まると思います。
さて、この手順でAD変換を行っていこうと思うのですが今回はArduino関数analogRead()のように関数化していこうかと思います。
AD変換のテスト回路は100kΩくらいの可変抵抗をADC2(PB4)に接続したシンプルな回路で行こうと思います。
早速ですがサンプルプログラムを以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//8bitアナログ変換 #include <avr/io.h> #include <BasicSerial3.h> #include <util/delay.h> uint8_t analog; uint8_t ADread(uint8_t ch) { ADCSRA |= 0b10000110;//AD許可,単独変換,AD変換CK/64 ADMUX |= 0b00100000;//Vcc基準,左揃え ADMUX |= ch; //ADC番号を引数とする ADCSRA |= 0b01000000;//AD変換開始 loop_until_bit_is_set(ADCSRA,ADIF);//変換完了フラグが1になるまで待機 return ADCH;//上位8bit取得(0-255) } void serOut(const char* str) { while (*str) {TxByte (*str++);} } // 整数を10進数で出力 void OutDEC(uint16_t d) { int8_t n =-1; uint16_t v = 10000; for (uint8_t i=0; i<5; i++) { if (d >= v) { TxByte(d/v + '0'); d %= v; n=i; } else { if (n!=-1||i==4) TxByte ('0'); } v/=10; } } void setup() { } void loop() { _delay_ms(500); analog = ADread(2); OutDEC(analog); serOut("\n\r"); } |
注意したいのはこのAD変換関数は8bit(0-255)対応になります。
説明するとAD変換の値はADCL, ADCHの上下8bitづつに格納される形になります。今回は左詰めで格納レジスタを設定したのでADCHのみ返り値として設定しております。10bit分解能については次回取り扱っていこうと思いますね。
考察
こちらも容量の比較を行ってみましょう。比較するのはanalogRead()と今回作成したADread()関数になります。
analogRead() | ADread() | |
フラッシュメモリ | 90 | 54 |
うん、やはりなかなかの効果です。半分近くは容量削減に貢献したと思います。
次回紹介する10bitのAD変換の場合はここに上下のレジスタを組み合わせる処理が入ってくるので若干容量アップしそうですね。
おわりに
今回はATtiny13を使いこなすためにもレジスタ操作によりArduino関数であるdigitalRead(), analogRead()を再現してみました。結果的にかなーり容量量削減に貢献できたと思います。この調子で容量削減しながらコードを構築できれば電子工作の幅が広がりそうでワクワクしてきましたね!
次回はanalogRead()の10bit版を再現してみますよ!
ではでは~
コメント