前提・実現したいこと
- Arduino
- if文を簡潔にして処理速度を向上させたい。それによって低消費電力にしたい。
発生している問題・エラーメッセージ
- ifが長すぎる
該当のソースコード
c++
1 int alarm_h = 6; 2 int alarm_m = 30; 3 int value = analogRead( A0 ); 4 int resistance = 1023 - value; 5 6 if (resistance <= 128) { 7 alarm_h = 6; 8 alarm_m = 0; 9 } else if (resistance <= 256) { 10 alarm_h = 6; 11 alarm_m = 30; 12 } else if (resistance <= 384) { 13 alarm_h = 6; 14 alarm_m = 45; 15 } else if (resistance <= 512) { 16 alarm_h = 7; 17 alarm_m = 0; 18 } else if (resistance <= 768) { 19 alarm_h = 7; 20 alarm_m = 30; 21 } else if (resistance <= 896) { 22 alarm_h = 8; 23 alarm_m = 0; 24 } else if (resistance <= 1023) { 25 alarm_h = 8; 26 alarm_m = 30; 27 }
試したこと
- swich文で記述する方法を検討した。
既にスリープは使っております。
補足情報(FW/ツールのバージョンなど)
Arduinoです。
alarm_h,alarm_mにA0の値に応じて代入しています。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2022/01/04 23:36
2022/01/05 01:11
退会済みユーザー
2022/01/05 01:43
2022/01/05 02:36
退会済みユーザー
2022/01/05 02:37
2022/01/05 02:47
2022/01/05 02:50
2022/01/05 02:52
2022/01/14 03:27
回答8件
0
質問の処理で「十分」に思えます。そこまで処理速度を要求するなら、アセンブラで書けばいい、と思うくらいに。
敢えて提案するなら、テーブルをつくりましょうか。これ、ワタシ的には速度よりも処理時間が入力によらず一定なことが「キレイ」に見えます。
C
1int db[][2]={{6,0},//128 2 {6,30},//256 3 {6,45},//384 4 {7,0},//512 5 {7,30},//640 6 {7,30},//768 7 {8,0},//896 8 {8,30}//1023 9}; 10 11int tmp=(resistance-1)>>7;//明示的に2^nの割り算にする。一般の割り算は多分とても遅い 12//修正。お題がresistance<=128, <=256...だから1引かないとですね) 13alarm_h=db[tmp][0]; 14alarm_m=db[tmp][1];
gccの最適化に任せるとさらにいろいろやってくれそうな気もします...が、ArduinoIDEで最適化を変えるのはちょっと面倒か。
ちょっと視点が変わってしまいますが
処理速度を向上させたい
という要求は妥当なのでしょうか? もしかして、処理は十分に間に合っているのに「無駄な努力、あるいは趣味」に精を出していたりしないでしょうか。Arduino UNOであれば16MHzのCPUで、(機械語レベルで)ほぼ1クロック1命令だったはずですから、多少の命令を削ってもその変化はマイクロ秒のオーダーです。これが処理に影響を与えるのですか?
(最近故あって4MHzのZ80:ほぼ4クロック1命令で数千万回繰り返す処理を書いたりしましたが、このくらいだとちょっと命令をけずると全体で半日の処理が10分早くなる、とかの変化があったりします)
本当に速度が足りないのなら、まず対策すべきはanalogRead()の部分では。最近もちょっと話題がありましたが、Arduinoはいろいろな機種でソースレベルでの互換性をとるという思想もあってか、カリカリなチューンをしているものではありません。そして、そのスジでは、UNO系のanalogRead()の遅さは有名で、高速化の試みも多数(かな?)為されています。必要なら調べてみてください。'Arduino analogread 高速化'ぐらいで検索すればいろいろ引っかかると思います。
投稿2022/01/04 22:21
編集2022/01/04 23:39総合スコア7703
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/01/04 22:29
2022/01/05 01:09
2022/01/05 02:44
0
条件ごとの代入値に一貫した共通点がないので、質問の状態が一番簡潔だと思います。
書き換えるとしてもこんな感じでしょうか。
c
1const int hs[8] = [ 6, 6, 6, 7, 7, 7, 8, 8]; 2const int ms[8] = [ 0, 30, 45, 0, 0, 30, 0, 30]; 3 4if (resistance <= 1023) { 5 int t = (resitance - 1) / 128; 6 alarm_h = hs[t]; 7 alarm_m = hs[m]; 8}
もしくは
c
1if (resistance <= 128) { 2 alarm_h = 6; 3 alarm_m = 0; 4} else if (resistance <= 384) { 5 alarm_h = 6; 6 alarm_m = (resistance - 1) / 128 * 15 7} else if (resistance <= 768) { 8 alarm_h = 7; 9 alarm_m = (resistance - 385) / 128 * 30 10} else if (resistance <= 1023) { 11 alarm_h = 8; 12 alarm_m = (resistance - 769) / 128 * 30 13}
こんなふうに構成し直しても速度に影響がでるのかわかりませんが、あまり、簡潔になったとは言い難いし、可読性も落ちましたね。
投稿2022/01/04 15:44
編集2022/01/05 01:11総合スコア194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/01/04 17:00
2022/01/04 18:33 編集
2022/01/04 23:08
2022/01/04 23:14
2022/01/05 01:35
2022/01/05 02:47
0
外部変数で
const byte h[8] = [ 8, 8, 7, 7, 7, 6, 6, 6];
const byte m[8] = [ 30, 0, 30, 0, 0, 45, 30, 0];
byte alarm_value;
としておいて、読み取りは
byte alarm_value = analogRead( A0 )>>7;
で、
①そのまま続けて
alarm_h = h[alarm_value];
alarm_m = m[alarm_value];
と代入するか、或いは
②代入せずに使用時に、h[alarm_value]とm[alarm_value]を使うか。
(alarm_hとalarm_mは使用しない)
のいずれかでは?
それ以前に疑問なのがADC値です。
ロータリースイッチを同じ抵抗値で繋いでいる雰囲気なのですけど、
『その場合の』期待値は
0,170.5,341,511.5,682,852.5,1023
です。特に真ん中の512(511.5)の部分の切り分けが怪しくなります。
byte alarm_value = (analogRead( A0 )-85)/170
のような式が適当になります。
その場合は上記の配列の重なったデータは消してデータ数は7です。
投稿2022/01/05 02:58
編集2022/01/05 03:49総合スコア494
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/01/05 03:23
2022/01/05 03:49
2022/01/05 03:52
0
こういう ifelseif ... の羅列のコンパイル結果がどのようになるのか全く知らないど素人ですが,
仮に「実際上の resistance のとる値の頻度に結構な差がある」ような場合,ifの順序を変えると平均的に速度が向上する(条件判定回数が減る)なんてことは無いのでしょうか?
または,2分探索みたく「最初に512の上下で分けて…」という形にすると平均的にマシになるとか.
投稿2022/01/05 02:53
総合スコア11954
0
Arduinoはわかりません。
C++も最低限しかわかってないのでおかしなところがあったらすみません。
128で割った時の切り上げの結果を元に、switch文で処理してます。
速度への影響はわかりませんが、if文よりはいくらかましなんですかね。
resistanceは0~1023を考慮すればいいのですかね。
c++
1#include <bits/stdc++.h> 2using namespace std; 3 4int main() { 5 int alarm_h = 6; 6 int alarm_m = 30; 7 vector<int> resistances = {0, 1, 127, 128, 129, 255, 256, 257, 8 383, 384, 385, 511, 512, 513, 767, 768, 9 769, 895, 896, 897, 1022, 1023, 1024}; 10 int ans; 11 for(int i = 0; i < resistances.size(); i++) { 12 cout << resistances[i] << " "; 13 ans = (resistances[i] + 128 - 1) / 128; 14 cout << ans; 15 16 switch(ans) { 17 case 0: // if (resistance <= 128) { 18 case 1: 19 alarm_h = 6; 20 alarm_m = 0; 21 cout << " A h:" << alarm_h << " m:" << alarm_m << endl; 22 break; 23 case 2: // if (resistance <= 256) { 24 alarm_h = 6; 25 alarm_m = 30; 26 cout << " B h:" << alarm_h << " m:" << alarm_m << endl; 27 28 break; 29 case 3: // else if (resistance <= 384) 30 alarm_h = 6; 31 alarm_m = 45; 32 cout << " C h:" << alarm_h << " m:" << alarm_m << endl; 33 break; 34 case 4: // else if (resistance <= 512) 35 alarm_h = 7; 36 alarm_m = 0; 37 cout << " D h:" << alarm_h << " m:" << alarm_m << endl; 38 break; 39 case 5: // else if (resistance <= 768) 40 case 6: 41 alarm_h = 7; 42 alarm_m = 30; 43 cout << " E h:" << alarm_h << " m:" << alarm_m << endl; 44 break; 45 case 7: // else if (resistance <= 896) 46 alarm_h = 8; 47 alarm_m = 0; 48 cout << " F h:" << alarm_h << " m:" << alarm_m << endl; 49 break; 50 case 8: // else if (resistance <= 1023) 51 alarm_h = 8; 52 alarm_m = 30; 53 cout << " G h:" << alarm_h << " m:" << alarm_m << endl; 54 break; 55 default: 56 break; 57 } 58 } 59} 60 61
実行結果
0 0 A h:6 m:0
1 1 A h:6 m:0
127 1 A h:6 m:0
128 1 A h:6 m:0
129 2 B h:6 m:30
255 2 B h:6 m:30
256 2 B h:6 m:30
257 3 C h:6 m:45
383 3 C h:6 m:45
384 3 C h:6 m:45
385 4 D h:7 m:0
511 4 D h:7 m:0
512 4 D h:7 m:0
513 5 E h:7 m:30
767 6 E h:7 m:30
768 6 E h:7 m:30
769 7 F h:8 m:0
895 7 F h:8 m:0
896 7 F h:8 m:0
897 8 G h:8 m:30
1022 8 G h:8 m:30
1023 8 G h:8 m:30
1024 8 G h:8 m:30
投稿2022/01/04 16:27
編集2022/01/04 16:36総合スコア509
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/01/04 17:02
2022/01/04 17:18
0
if分を並べるのではなく、switch~caseを使うなら以下の様にも書けます。
C/C++
1int res_step = (resistance - 1)/128; 2if (res_step < 0) { 3 res_step = 0; // resistance=0のとき負になるのを防ぐ 4} 5switch (res_step) { 6case 0: // resistance <= 128 7 alarm_h = 6; 8 alarm_m = 0; 9 break; 10case 1: // 128 < resistance <= 256 11 alarm_h = 6; 12 alarm_m = 30; 13 break; 14case 2: // 256< resistance <= 384 15 alarm_h = 6; 16 alarm_m = 45; 17 break; 18case 3: // 384 < resistance <= 512 19 alarm_h = 7; 20 alarm_m = 0; 21 break; 22case 4: // 512 < resistance <= 640 ※ミスではありません 23case 5: // 640 < resistance <= 768 24 alarm_h = 7; 25 alarm_m = 30; 26 break; 27case 6: // 768 < resistance <= 896 28 alarm_h = 8; 29 alarm_m = 0; 30 break; 31case 7: // 896 < resistance <= 1024 32 alarm_h = 8; 33 alarm_m = 30; 34 break; 35default: 36 // 書かなくてもいいかもしれませんが、適当に 37 break; 38}
以下を利用しています。
- 条件分岐がほぼ128刻みになっていること
- 整数型(resistance)の除算は小数点以下が切り捨てられること
- caseラベルにジャンプ後は、breakまたはswitchブロックの終わりまで実行されること
1行目は境界値の調整、
2~4行目は例外の対応です。
この場合、resistance=1024だった場合もresistance=1023の場合と同じ動作になる点が、提示されたコードと異なります。
そのようなケースにどうするべきかは提示されていないので、適当に処理してください。
目的である処理速度向上や消費電力の削減にどれくらい影響するかは未知数です。
投稿2022/01/22 15:09
総合スコア106
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/01/24 15:22
0
前後のコードが無いと何をしているのかさっぱり分かりませんが(そもそもなぜこの部分だけを切り出したのでしょうか)、
少なくとも示されたコードの中ではanalogRead内でビジーループしているのが一番の無駄だと思います。
どのArduinoか分かりませんがソースはこの辺でいいのかな。たぶんどの機種でも同様だと思います。
ArduinoCore-avr/cores/arduino/wiring_analog.c
while (bit_is_set(ADCSRA, ADSC)); ```ADC待ちの間に他のコードを入れてやるのがよいでしょう。
投稿2022/01/05 16:50
総合スコア3047
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。