質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

8回答

2401閲覧

if文を簡潔に記述したい

yuto0315

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

1クリップ

投稿2022/01/04 15:27

編集2022/01/05 02:39

前提・実現したいこと

  • 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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2022/01/04 18:16

C# は関係ないのではないでしょうか。
退会済みユーザー

退会済みユーザー

2022/01/04 23:36

C# のタグは外してください。タグで探して見ている人にはノイズになって迷惑です。
yuto0315

2022/01/05 01:11

あわわわわ、すみません 間違って付いていましたぁぁ
退会済みユーザー

退会済みユーザー

2022/01/05 01:43

低消費電力を考慮するなら、変にコード弄り回すより適切なスリープを入れたりといった事を考えるべきだと思いますが。
yuto0315

2022/01/05 02:36

既にスリープは使っております。 その結果27μAまで消費電力は落ちました。 情報漏れ申し訳ない
退会済みユーザー

退会済みユーザー

2022/01/05 02:37

エスパーじゃないので書いてない事までは判らんです。 既にやった事などは質問文に追記しといてください。
fana

2022/01/05 02:47

こういう if~elseif~ ... の羅列のコンパイル結果がどのようになるのか全く知らないど素人ですが, 仮に「実際上の resistance のとる値の頻度に結構な差がある」ような場合,ifの順序を変えると平均的に速度が向上する(条件判定回数が減る)なんてことは無いのでしょうか? または,2分探索みたく「最初に512の上下で分けて…」という形にすると平均的にマシになるとか.
yuto0315

2022/01/05 02:50

頻度はかなりランダムですね。 2分探索って手段良さげですね。 ぇ、これって回答に書かなくても大丈夫ですか? 自分は全然良いんですけど、、、
fana

2022/01/05 02:52

(そうですね,こういう話は他の有識者からのご指摘等もあり得るかもしれないので,同内容を回答として書いておきます)
tmp

2022/01/14 03:27

switch文で最適化を期待するなら、caseの数値が連続するような値にすると、テーブル作ってジャンプするようなコードができると思いますが、どのようにswitch文を検討しましたか?
guest

回答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
thkana

総合スコア7639

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2022/01/04 22:29

あぁ、電力ですか...共通な事項は質問に追記しておいて欲しいなぁ。 つまり、合間々々にスリープするから処理時間は少しでも短いほうがいい、ということですか? それにしても、影響度合いはちゃんと評価すべきでしょうね。
yuto0315

2022/01/05 01:09

すいません、実はteratail初使用で、、笑 そうですね、処理にかかる時間が少ない方が嬉しいです
yuto0315

2022/01/05 02:44

読ませていただきました。 確かに処理時間が一定なのが”キレイ”な気もします。 まだまだプログラミング始めたばかりでどのような手段、選択肢があるのか把握しきれてないので何かほかに良い案があるのでは?と思ってました。 これ以上は確かに過剰なのかもしれません。
guest

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
wsb

総合スコア194

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuto0315

2022/01/04 17:00

個人的な目標としてatmega328マイコンの消費電力を落としつつ、速度向上を目標にしているので可読性は二の次って感じでやってます。(何とか27μAまで辿り着きました、、、) これ以上は難しそうですねぇ
jimbe

2022/01/04 18:33 編集

value が 0~1023 しかなり得なければ(analogRead は 10 ビットですよね)、テーブル版のほうで if 文を除けるのではないでしょうか。 (alarm_m = hs[m] → alarm_m = ms[t] )
thkana

2022/01/04 23:08

仕様書上ですが、ATMega328の消費電流が5V16MHzで9mA。スリープ時の電流は常温下では無視できるとして(仕様書Figure29-3)、平均の消費電流が27μAということは、CPUの稼働率が0.3%、1秒あたり3mSの動作ということですね?
Zuishin

2022/01/04 23:14

あと alarm_h と alarm_m を構造体にまとめれば代入が一度で済むので何か変わるかもしれません。
dodox86

2022/01/05 01:35

既にいただいている複数の回答をベースに、それら複数のパターンで再度測定してみては?
yuto0315

2022/01/05 02:47

そうですね、これまでの内容で1度計測してみます。 初心者すぎて手段、選択肢すら思いつかなかったので皆さんにすごく助けられています、、
guest

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
nac_tnk

総合スコア463

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuto0315

2022/01/05 03:23

A0は可変抵抗(ポテンションメーター?)に繋いでます。 期待値は〜のところがよく分からなかったのですが、、、 もしよろしければそこだけ詳しくお願いできますか?
nac_tnk

2022/01/05 03:49

添付図(↑)のようにロータリースイッチを配線していた場合の話です。 その場合は均等に分割されるので上記のように、1CH切り替える毎に1023/6=170.5が 1CH変えた際に変動する、という事です。
thkana

2022/01/05 03:52

> A0は可変抵抗(ポテンションメーター?)に繋いでます 可変抵抗に電流流しっぱなしとか、切ってても使用時に相対的に大きな電流を流してたり、なんてことはないですよね? (情報が小出しだからいちいち反応するのも疲れるねぇ)
guest

0

こういう ifelseif ... の羅列のコンパイル結果がどのようになるのか全く知らないど素人ですが,
仮に「実際上の resistance のとる値の頻度に結構な差がある」ような場合,ifの順序を変えると平均的に速度が向上する(条件判定回数が減る)なんてことは無いのでしょうか?

または,2分探索みたく「最初に512の上下で分けて…」という形にすると平均的にマシになるとか.

投稿2022/01/05 02:53

fana

総合スコア11658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2022/01/05 02:53

([質問への追記・修正、ベストアンサー選択の依頼] に書いた文章そのままです)
guest

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
Crimson_Tide

総合スコア509

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuto0315

2022/01/04 17:02

ifよりSwitchの方が速度は早いみたいですねぇ。 割って切り上げの動作が入るとどっちの方が早いんでしょうか、、、
Crimson_Tide

2022/01/04 17:18

申し訳ないですが、実機で確認していただく以外は影響度合いはわかりかねます。 なるほどマイコンだと計算処理1つ増えるだけでも消費電力や処理速度の影響が大きそうですね。 その辺り考慮していませんでした。 これは専門の方にお任せしたほうがよさそうですね。
guest

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

yossie

総合スコア106

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yossie

2022/01/24 15:22

追記です。 switch~caseを使った場合、多くのコンパイラは下記の様な「if文の羅列」のコードを生成します。 ``` cmp res_step, 0 jz case_0 sub res_step,1 ←0と1の差分 jz case_1 ... jmp case_end case_0 0の場合の処理 jmp case_end case_1 1の場合の処理 jmp case_end ... ``` 多くのプロセッサはswitch~caseに直接対応するような命令を持っていません。 そのため上記の様に比較と条件分岐を繰り返すのが、最も速く、短くなるようです。 無理矢理switch~caseを使うくらいなら、if~else ifの羅列になっても、可読性が高い方が望ましいと思います。 私が示したように無理にcaseに合致するように値を操作するのは、可読性・保守性の点で劣ります。 「こんな書き方もできる」くらいの参考にしてください。
guest

0

alarm_h =table_h[value]
alarm_m =table_m[value]

ROM容量を考えないなら、単純にそのままテーブルを作れば、resistanceの引き算も不要で、シフト演算も不要。
※if文には、イコールが付いてるから単純にシフト演算に置き換えれないところも解決する
結局、いろんな条件を書かないとわからないよ

投稿2022/01/14 03:40

tmp

総合スコア277

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

前後のコードが無いと何をしているのかさっぱり分かりませんが(そもそもなぜこの部分だけを切り出したのでしょうか)、
少なくとも示されたコードの中ではanalogRead内でビジーループしているのが一番の無駄だと思います。

どのArduinoか分かりませんがソースはこの辺でいいのかな。たぶんどの機種でも同様だと思います。
ArduinoCore-avr/cores/arduino/wiring_analog.c

while (bit_is_set(ADCSRA, ADSC)); ```ADC待ちの間に他のコードを入れてやるのがよいでしょう。

投稿2022/01/05 16:50

ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問