Arduinoでダイナミック点灯を制御していると思うのが「Arduinoって遅いなあ」ということです。
私は最初、16MHzだからあまり困ることはないと思っていましたが、最近困ることが多いです。
スポンサーリンク
調べてみるとdigitalWriteは実行するのに44サイクルかかるらしいです。
他にもdigitalReadやpinModeなんかも結構遅い感じでした。
そこでAVR寄りの書き方にすることで自分なりに高速化することにしました。
高速化の原理?
高速化するといっても簡単にいえばArduinoの冗長なプログラムを短くするだけです。
Arduinoはご存知の通り、ATmega328Pが搭載されています。
そのATmega328PとArduinoのピンの対応付けは次の図のようになっています。
ICの11番ピンのPD5はArduinoのディジタル5番ピンに割り当てられています。
AVRのプログラムの場合はレジスタを制御することによってこういうピンの状態をHIGHにしたりLOWにしたりできるのです。
Arduinoはレジスタ制御をうまいこと隠すことによってわかりやすいプログラムになっており、互換性のあるプログラムにもなっているのです。
この隠すためにしている処理を省くことによって高速ができます。
まぁ難しいことは言いたくないのでここではこれ以上言いません。
知りたくなったら違うサイトで調べてください。
digitalWrite:デジタル出力
digitalWriteは先ほど言ったように44サイクルを要します。
これを次のように書くことで、3サイクルまで抑えることができます。
PORTD |= _BV(5);
これはArduino的に書くと、
digitalWrite(13, HIGH);
と同じなのです。
上の図で言うとPD5をHIGHにしたという感じです。
PBのところを動かしたい場合はPORTB、PCのところを動かしたい場合はPORTCにすればいいです。
ちなみにdigitalWirteで言うLOWにしたい時は
PORTD &= ~_BV(5);
とするとLOWが出力されます。
複数のピンを操作する場合は
PORTD |= _BV(5) | _BV(6) | _BV(7); PORTD &= ~(_BV(5) | _BV(6) | _BV(7));
と書いたりできます。
これはほんの一例で、他にも様々な書き方があります。
PORTD |= 0b1010000;
こうすることで1の部分だけHIGHにすることができます。(0の部分は何も変わりません)
PORTD = 0b1010000;
こうすれば一度に1の部分はHIGH、0の部分はLOWにすることができますが、全て変更してしまうので使いどころが難しいです。
digitalRead:デジタル入力
個人的にはあまり使わないのですが、高速化する方法はあります。
通常は以下のような書き方をすると思います。
int val = digitalRead(2);
これを次のような書き方にすることで、これまた大幅に高速化できます。
int val = PIND & _BV(2);
一気に複数入力することもできますが、あまり使わないので紹介しないことにします。
検証
書き方を変えただけでどれくらい変わったのか見てみようと思います。
比較したのは次の2つのプログラムです。
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); digitalWrite(13, LOW); }
void setup() { pinMode(13, OUTPUT); } void loop() { PORTB |= _BV(5); PORTB &= ~_BV(5); }
スポンサーリンク
この2つのプログラムはどちらも13ピンをHIGH→LOWと交互にするだけのプログラムです。
標準で入っているLチカプログラムのBlinkのdelayが無いバージョンです。
digitalWriteで記述した方の13ピンの出力波形を見てみるとこんな感じになりました。
周波数はおよそ144Hzです。
計算だと180kHzぐらいになるはずなんですけど・・・
関数のオーバーヘッドとかが原因なんでしょうかね?
delay関数を入れていないのにこの遅さになってしまいます。
PORTで記述した方の13ピンの出力波形は次のようになりました。
周波数はおよそ2MHzになり断然速いのがわかります。
紹介したのは digitalWrite と digitalRead だけでしたが、他にもまだまだ他にもたくさん改善するプログラムはあります。
Arduinoに慣れて簡単になってきたら、マイコン寄りのプログラムにも慣れていくことをおすすめします。
そうすることでAVR以外のマイコンにも簡単に触ることができますし、もっと電子工作が楽しくなるのではないかと思っています。
といってもそう言っている本人が、現段階であんまり理解できてないんですけどね(´・ω・`)
ADC(アナログ入力)やPWM(アナログ出力)の高速化は以下の記事で紹介しています。
スポンサーリンク
クールです!
参考にさせていただきます。
お世話になります。
現在、Leonardoを使用しており、同様に高速化を検討しております。
Leonard0にはATmega32U4が実装されていますが、Arduinoのピンの対応付けを記した資料を見つけることが出来ませんでした。
もし、対応付けをした資料が掲示されているHP等をご存知でしたら、ご連絡頂きたいと思います。
現在は改善されているかも知れませんが、お役に立てば幸いです。
http://www-ice.yamagata-cit.ac.jp/forum/viewtopic.php?t=820
Pingback: バーサライタの点灯プログラム - jp3cyc's blogjp3cyc's blog
Pingback: Arduinoの動きを高速化する
一気に複数入力する方法をご教授頂けないでしょうか?
あるディーノ 様
一気に複数入力する方法ですが、
そもそもPIND自体がPIND0~7の8ピン分の情報を持っています。
複数条件の場合は、PINDの値がどうなっているかで判断できます。
記述としては
if (PIND == 0b11001100)
みたいな感じになります。
もしくは出力と同じようにビット演算を使って複数のビットを抽出するのもありです。