前述しました様に、限られたメモリーと限られた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 吉川



