前述しました様に、限られたメモリーと限られたCPU資源による組込みプログラムは製品化には必須条件です。
特に、Arduinoは便利な標準関数が多く用意されていますが、じつはこの便利関数はオーバーヘッドをがかなり大きくて処理時間やメモリー使用量に影響したりします。
その中で最初に簡単にできる対策として、ポートの入出力関数[digitalWrite]]digitalRead][analogRead]の置き換えが有効手段としてあります。
これについてまとめてみます。
digitalWrite/digitalRead のスリム化と高速化
IOポートを簡単に制御するために用意されているこの2つの関数は、実は別の方法によって速度は70%ほど向上します。
digitalWriteを使った、単純なプログラムで、8番ピンから13番ピンまでにHIGHを出力するプログラムは下記のようになります。
digitalWriteによるプログラム
int port[]={8,9,10,11,12,13}; //ポートの初期設定 void setup() { int i; for(i=0;i<6;i++){ pinMode(port[i],OUTPUT); } } //動作 void loop() { //forでまわさずにあえて1行ずつ書いてみました digitalWrite(port[0],HIGH); digitalWrite(port[1],HIGH); digitalWrite(port[2],HIGH); digitalWrite(port[3],HIGH); digitalWrite(port[4],HIGH); digitalWrite(port[5],HIGH); }
ポート直接制御のプログラム
//書き方1 マクロを使わない void setup() { DDRB = DDRB | B11100000; //1で出力 0 で入力 DDRD = DDRD | B01000000; DDRC = DDRC | B10000000; } void loop() { PORTB = B11100000; //出力に指定したポートに1を出力するとHIGH 0でLOW PORTD = B01000000; PORTC = B10000000; }
//書き方2 マクロを使う void setup() { DDRB = DDRB | B11100000; //1で出力 0 で入力 DDRD = DDRD | B01000000; DDRC = DDRC | B10000000; } void loop() { PORTB |= _BV(7)|BV(6)|BV(5); PORTD |= _BV(6); PORTC |= _BV(7)|; } //ちなみにLOWを出力する場合はこのようにも書きます void setup() { DDRB = DDRB | B11100000; //1で出力 0 で入力 DDRD = DDRD | B01000000; DDRC = DDRC | B10000000; } void loop() { PORTB &= ~(_BV(7)|~_BV(6)|~_BV(5)); PORTD &= ~_BV(6); PORTC &= ~_BV(7); }
上記2つは同じ処理をしています。
書き方2ではマクロを使っていますので人によってはかき方1のほうがわかりやすかったりします。
PIC系の方は書き方1が慣れ親しんでいるのではないでしょうか?
速度のと処理の違い
digitalWriteを使用した場合1行で1ポートの制御しかできませんが、ポート直接制御を利用した場合は、1行で複数ポートの制御が可能です。
また、digitalWriteは1行で44サイクル、約4~5μ秒かかりますが、ポート直接制御では1行で3サイクルしかかかりません。
これだけでも相当な処理速度の改善が図れます。
本格的に組込みプログラミングを行う場合にはまちがえなく、上記のポート直接制御を使うべきです。
処理命令のまとめ
上記に示しました digitalWriteのほかに、入力としてのdigitalReadも同じように置き換えることで高速化スリム化ができます。
また、IOポート利用時はあらかじめ pinModeでピンの入出力も設定しておく必要がありますが、これもポート直接命令では記述がかわります。
下記に整理しておきます。
pinModeの置き換え公式
公式 | DDR(N) = B******** / DDR(N) = DDR(N) | B******** |
例 | DDRB = DDRB | B11100000 |
説明 | (N)…対象となる出力ピンが存在するポートのアルファベット文字 (*)…1で出力 0で入力を指定 |
digitalWriteの置き換え公式
公式 | PORT(N) = B********* / PORT(N) = PORT(N) | B******* PORT(N) = _BV($) / PORT(N) = PORT(N) | _BV($) PORT(N) = ~_BV($) / PORT(N) = PORT(N) | ~_BV($) |
例 | PORTB = B1100000; PORTB = PORTB | B1100000 PORTC = _BV(7) | _BV(6) ; PORTC = PORTC | _BV(7) | _BV(6) ; PORTB &= ~(_BV(7)|~_BV(6)|~_BV(5)); |
説明 | (N)…対象となる出力ピンが存在するポートのアルファベット文字 (*)…1でHIGH 0でLOW _BV($)…指定した$番にHIGHを出力 ~_BV($)…指定した$番にLOWを出力 |
digitalReadの置き換え公式
公式 | val = PIN(N) & _BV($) |
例 | int val = PINB & _BV(7) |
説明 | (N)…対象となる出力ピンが存在するポートのアルファベット文字 _BV($)…指定した$番のピン状態を読み込む |
ポート直接制御のためにはピン配置とポートの関係を理解する
ポート直接制御のためにはポートとピンの関係を理解しなくてはいけません。
下記にその関係を整理しておきます。
Type:UNO,Pro(5V),Pro(3V),ProMini,Nano,LilyPadMain,LilyPadSimple,LilyPadSimpleSnap |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CPU & port:328P/168 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arduinoのピン番号からみたポート割付(D=Digital,A=AnlogInput)Arduinoのボードにシルク印刷されているピン番号を元にどのポートにわりあてられているかを見る表です。
ポートからみたArduinoのピン番号Arduinoのポート側からみてどの出力ピンに対応するかを見る逆表です。
|
Type:MEGA | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CPU & port:2560 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arduinoのピン番号からみたポート割付(D=Digital,A=AnlogInput)Arduinoのボードにシルク印刷されているピン番号を元にどのポートにわりあてられているかを見る表です。
ポートからみたArduinoのピン番号Arduinoのポート側からみてどの出力ピンに対応するかを見る逆表です。
|
最後に
このように、付属する便利関数はとても便利なのですが、やはりハードウエアに近い部分で制御するほうが、絶対的に速度があがり組込みには必要なことがわかります。
参考までに、pinModeとdigitalWriteがどれだけの処理で構成されているか、Arduinoの関数の中を下記に公開します。
これをみるだけでも十分な高価は理解できると思われます。
void pinMode(uint8_t pin, uint8_t mode) { uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); volatile uint8_t *reg, *out; if (port == NOT_A_PIN) return; // JWS: can I let the optimizer do this? reg = portModeRegister(port); out = portOutputRegister(port); if (mode == INPUT) { uint8_t oldSREG = SREG; cli(); *reg &= ~bit; *out &= ~bit; SREG = oldSREG; } else if (mode == INPUT_PULLUP) { uint8_t oldSREG = SREG; cli(); *reg &= ~bit; *out |= bit; SREG = oldSREG; } else { uint8_t oldSREG = SREG; cli(); *reg |= bit; SREG = oldSREG; } } void digitalWrite(uint8_t pin, uint8_t val) { uint8_t timer = digitalPinToTimer(pin); uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); volatile uint8_t *out; if (port == NOT_A_PIN) return; // If the pin that support PWM output, we need to turn it off // before doing a digital write. if (timer != NOT_ON_TIMER) turnOffPWM(timer); out = portOutputRegister(port); uint8_t oldSREG = SREG; cli(); if (val == LOW) { *out &= ~bit; } else { *out |= bit; } SREG = oldSREG; }
2016.1.28 吉川